diff options
Diffstat (limited to 'js/src/gc/GCParallelTask.cpp')
-rw-r--r-- | js/src/gc/GCParallelTask.cpp | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/js/src/gc/GCParallelTask.cpp b/js/src/gc/GCParallelTask.cpp new file mode 100644 index 0000000000..8162e33000 --- /dev/null +++ b/js/src/gc/GCParallelTask.cpp @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gc/GCParallelTask.h" + +#include "mozilla/MathAlgorithms.h" + +#include "gc/ParallelWork.h" +#include "vm/HelperThreadState.h" +#include "vm/Runtime.h" +#include "vm/TraceLogging.h" + +using namespace js; +using namespace js::gc; + +using mozilla::TimeDuration; +using mozilla::TimeStamp; + +js::GCParallelTask::~GCParallelTask() { + // Only most-derived classes' destructors may do the join: base class + // destructors run after those for derived classes' members, so a join in a + // base class can't ensure that the task is done using the members. All we + // can do now is check that someone has previously stopped the task. + assertIdle(); +} + +void js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock) { + MOZ_ASSERT(CanUseExtraThreads()); + MOZ_ASSERT(!HelperThreadState().threads(lock).empty()); + assertIdle(); + + setDispatched(lock); + HelperThreadState().submitTask(this, lock); +} + +void js::GCParallelTask::start() { + if (!CanUseExtraThreads()) { + runFromMainThread(); + return; + } + + AutoLockHelperThreadState lock; + startWithLockHeld(lock); +} + +void js::GCParallelTask::startOrRunIfIdle(AutoLockHelperThreadState& lock) { + if (wasStarted(lock)) { + return; + } + + // Join the previous invocation of the task. This will return immediately + // if the thread has never been started. + joinWithLockHeld(lock); + + if (!CanUseExtraThreads()) { + AutoUnlockHelperThreadState unlock(lock); + runFromMainThread(); + return; + } + + startWithLockHeld(lock); +} + +void js::GCParallelTask::cancelAndWait() { + MOZ_ASSERT(!isCancelled()); + cancel_ = true; + join(); + cancel_ = false; +} + +void js::GCParallelTask::join() { + AutoLockHelperThreadState lock; + joinWithLockHeld(lock); +} + +void js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock) { + // Task has not been started; there's nothing to do. + if (isIdle(lock)) { + return; + } + + // If the task was dispatched but has not yet started then cancel the task and + // run it from the main thread. This stops us from blocking here when the + // helper threads are busy with other tasks. + if (isDispatched(lock)) { + cancelDispatchedTask(lock); + AutoUnlockHelperThreadState unlock(lock); + runFromMainThread(); + return; + } + + joinRunningOrFinishedTask(lock); +} + +void js::GCParallelTask::joinRunningOrFinishedTask( + AutoLockHelperThreadState& lock) { + MOZ_ASSERT(isRunning(lock) || isFinished(lock)); + + // Wait for the task to run to completion. + while (!isFinished(lock)) { + HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER); + } + + setIdle(lock); +} + +void js::GCParallelTask::cancelDispatchedTask(AutoLockHelperThreadState& lock) { + MOZ_ASSERT(isDispatched(lock)); + MOZ_ASSERT(isInList()); + remove(); + setIdle(lock); +} + +static inline TimeDuration TimeSince(TimeStamp prev) { + TimeStamp now = ReallyNow(); + // Sadly this happens sometimes. + MOZ_ASSERT(now >= prev); + if (now < prev) { + now = prev; + } + return now - prev; +} + +void js::GCParallelTask::runFromMainThread() { + assertIdle(); + MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(gc->rt)); + AutoLockHelperThreadState lock; + runTask(lock); +} + +void js::GCParallelTask::runHelperThreadTask(AutoLockHelperThreadState& lock) { + TraceLoggerThread* logger = TraceLoggerForCurrentThread(); + AutoTraceLog logCompile(logger, TraceLogger_GC); + + setRunning(lock); + + AutoSetHelperThreadContext usesContext(lock); + AutoSetContextRuntime ascr(gc->rt); + gc::AutoSetThreadIsPerformingGC performingGC; + runTask(lock); + + setFinished(lock); +} + +void GCParallelTask::runTask(AutoLockHelperThreadState& lock) { + // Run the task from either the main thread or a helper thread. + + // The hazard analysis can't tell what the call to func_ will do but it's not + // allowed to GC. + JS::AutoSuppressGCAnalysis nogc; + + TimeStamp timeStart = ReallyNow(); + run(lock); + duration_ = TimeSince(timeStart); +} + +bool js::GCParallelTask::isIdle() const { + AutoLockHelperThreadState lock; + return isIdle(lock); +} + +bool js::GCParallelTask::wasStarted() const { + AutoLockHelperThreadState lock; + return wasStarted(lock); +} + +/* static */ +size_t js::gc::GCRuntime::parallelWorkerCount() const { + return std::min(helperThreadCount.ref(), MaxParallelWorkers); +} |