diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/gc/GCParallelTask.h | 246 |
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 */ |