summaryrefslogtreecommitdiffstats
path: root/js/src/gc/GCParallelTask.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/GCParallelTask.h')
-rw-r--r--js/src/gc/GCParallelTask.h246
1 files changed, 246 insertions, 0 deletions
diff --git a/js/src/gc/GCParallelTask.h b/js/src/gc/GCParallelTask.h
new file mode 100644
index 0000000000..4784382ab5
--- /dev/null
+++ b/js/src/gc/GCParallelTask.h
@@ -0,0 +1,246 @@
+/* -*- 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/. */
+
+#ifndef gc_GCParallelTask_h
+#define gc_GCParallelTask_h
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/TimeStamp.h"
+
+#include <utility>
+
+#include "gc/GCContext.h"
+#include "js/Utility.h"
+#include "threading/ProtectedData.h"
+#include "vm/HelperThreads.h"
+#include "vm/HelperThreadTask.h"
+
+#define JS_MEMBER_FN_PTR_TYPE(ClassT, ReturnT, /* ArgTs */...) \
+ ReturnT (ClassT::*)(__VA_ARGS__)
+
+#define JS_CALL_MEMBER_FN_PTR(Receiver, Ptr, /* Args */...) \
+ ((Receiver)->*(Ptr))(__VA_ARGS__)
+
+namespace js {
+
+namespace gcstats {
+enum class PhaseKind : uint8_t;
+}
+
+namespace gc {
+
+class GCRuntime;
+
+static inline mozilla::TimeDuration TimeSince(mozilla::TimeStamp prev) {
+ mozilla::TimeStamp now = mozilla::TimeStamp::Now();
+ // Sadly this happens sometimes.
+ MOZ_ASSERT(now >= prev);
+ if (now < prev) {
+ now = prev;
+ }
+ return now - prev;
+}
+
+} // namespace gc
+
+class AutoLockHelperThreadState;
+class GCParallelTask;
+class HelperThread;
+
+// A wrapper around a linked list to enforce synchronization.
+class GCParallelTaskList {
+ mozilla::LinkedList<GCParallelTask> tasks;
+
+ public:
+ bool isEmpty(const AutoLockHelperThreadState& lock) {
+ gHelperThreadLock.assertOwnedByCurrentThread();
+ return tasks.isEmpty();
+ }
+
+ void insertBack(GCParallelTask* task, const AutoLockHelperThreadState& lock) {
+ gHelperThreadLock.assertOwnedByCurrentThread();
+ tasks.insertBack(task);
+ }
+
+ GCParallelTask* popFirst(const AutoLockHelperThreadState& lock) {
+ gHelperThreadLock.assertOwnedByCurrentThread();
+ return tasks.popFirst();
+ }
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ const AutoLockHelperThreadState& lock) const {
+ gHelperThreadLock.assertOwnedByCurrentThread();
+ return tasks.sizeOfExcludingThis(aMallocSizeOf);
+ }
+};
+
+// A generic task used to dispatch work to the helper thread system.
+// Users override the pure-virtual run() method.
+class GCParallelTask : private mozilla::LinkedListElement<GCParallelTask>,
+ public HelperThreadTask {
+ friend class mozilla::LinkedList<GCParallelTask>;
+ friend class mozilla::LinkedListElement<GCParallelTask>;
+
+ public:
+ gc::GCRuntime* const gc;
+
+ // This can be PhaseKind::NONE for tasks that take place outside a GC.
+ const gcstats::PhaseKind phaseKind;
+
+ gc::GCUse use;
+
+ private:
+ // The state of the parallel computation.
+ enum class State {
+ // The task is idle. Either start() has not been called or join() has
+ // returned.
+ Idle,
+
+ // The task has been started but has not yet begun running on a helper
+ // thread.
+ Dispatched,
+
+ // The task is currently running on a helper thread.
+ Running,
+
+ // The task has finished running but has not yet been joined by the main
+ // thread.
+ Finished
+ };
+
+ UnprotectedData<State> state_;
+
+ // May be set to the time this task was queued to collect telemetry.
+ mozilla::TimeStamp maybeQueueTime_;
+
+ // Amount of time this task took to execute.
+ MainThreadOrGCTaskData<mozilla::TimeDuration> duration_;
+
+ explicit GCParallelTask(const GCParallelTask&) = delete;
+
+ protected:
+ // A flag to signal a request for early completion of the off-thread task.
+ mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire> cancel_;
+
+ public:
+ explicit GCParallelTask(gc::GCRuntime* gc, gcstats::PhaseKind phaseKind,
+ gc::GCUse use = gc::GCUse::Unspecified)
+ : gc(gc),
+ phaseKind(phaseKind),
+ use(use),
+ state_(State::Idle),
+ cancel_(false) {}
+ GCParallelTask(GCParallelTask&& other)
+ : gc(other.gc),
+ phaseKind(other.phaseKind),
+ use(other.use),
+ state_(other.state_),
+ cancel_(false) {}
+
+ // Derived classes must override this to ensure that join() gets called
+ // before members get destructed.
+ virtual ~GCParallelTask();
+
+ // Time spent in the most recent invocation of this task.
+ mozilla::TimeDuration duration() const { return duration_; }
+
+ // The simple interface to a parallel task works exactly like pthreads.
+ void start();
+ void join(mozilla::Maybe<mozilla::TimeStamp> deadline = mozilla::Nothing());
+
+ // If multiple tasks are to be started or joined at once, it is more
+ // efficient to take the helper thread lock once and use these methods.
+ void startWithLockHeld(AutoLockHelperThreadState& lock);
+ void joinWithLockHeld(
+ AutoLockHelperThreadState& lock,
+ mozilla::Maybe<mozilla::TimeStamp> deadline = mozilla::Nothing());
+ void joinNonIdleTask(mozilla::Maybe<mozilla::TimeStamp> deadline,
+ AutoLockHelperThreadState& lock);
+
+ // Instead of dispatching to a helper, run the task on the current thread.
+ void runFromMainThread();
+ void runFromMainThread(AutoLockHelperThreadState& lock);
+
+ // If the task is not already running, either start it or run it on the main
+ // thread if that fails.
+ void startOrRunIfIdle(AutoLockHelperThreadState& lock);
+
+ // Cancel a dispatched task before it started executing.
+ void cancelDispatchedTask(AutoLockHelperThreadState& lock);
+
+ // Set the cancel flag and wait for the task to finish.
+ void cancelAndWait();
+
+ // Report whether the task is idle. This means either before start() has been
+ // called or after join() has been called.
+ bool isIdle() const;
+ bool isIdle(const AutoLockHelperThreadState& lock) const {
+ return state_ == State::Idle;
+ }
+
+ // Report whether the task has been started. This means after start() has been
+ // called but before the task has run to completion. The task may not yet have
+ // started running.
+ bool wasStarted() const;
+ bool wasStarted(const AutoLockHelperThreadState& lock) const {
+ return isDispatched(lock) || isRunning(lock);
+ }
+
+ bool isDispatched(const AutoLockHelperThreadState& lock) const {
+ return state_ == State::Dispatched;
+ }
+
+ protected:
+ // Override this method to provide the task's functionality.
+ virtual void run(AutoLockHelperThreadState& lock) = 0;
+
+ virtual void recordDuration();
+
+ bool isCancelled() const { return cancel_; }
+
+ private:
+ void assertIdle() const {
+ // Don't lock here because that adds extra synchronization in debug
+ // builds that may hide bugs. There's no race if the assertion passes.
+ MOZ_ASSERT(state_ == State::Idle);
+ }
+
+ bool isRunning(const AutoLockHelperThreadState& lock) const {
+ return state_ == State::Running;
+ }
+ bool isFinished(const AutoLockHelperThreadState& lock) const {
+ return state_ == State::Finished;
+ }
+
+ void setDispatched(const AutoLockHelperThreadState& lock) {
+ MOZ_ASSERT(isIdle(lock));
+ state_ = State::Dispatched;
+ }
+ void setRunning(const AutoLockHelperThreadState& lock) {
+ MOZ_ASSERT(isDispatched(lock));
+ state_ = State::Running;
+ }
+ void setFinished(const AutoLockHelperThreadState& lock) {
+ MOZ_ASSERT(isRunning(lock));
+ state_ = State::Finished;
+ }
+ void setIdle(const AutoLockHelperThreadState& lock) {
+ MOZ_ASSERT(isDispatched(lock) || isFinished(lock));
+ state_ = State::Idle;
+ }
+
+ void runTask(JS::GCContext* gcx, AutoLockHelperThreadState& lock);
+
+ // Implement the HelperThreadTask interface.
+ ThreadType threadType() override {
+ return ThreadType::THREAD_TYPE_GCPARALLEL;
+ }
+ void runHelperThreadTask(AutoLockHelperThreadState& locked) override;
+};
+
+} /* namespace js */
+#endif /* gc_GCParallelTask_h */