summaryrefslogtreecommitdiffstats
path: root/js/src/gc/GCParallelTask.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/GCParallelTask.cpp')
-rw-r--r--js/src/gc/GCParallelTask.cpp173
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);
+}