GCParallelTask.h (8258B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef gc_GCParallelTask_h 8 #define gc_GCParallelTask_h 9 10 #include "mozilla/LinkedList.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/TimeStamp.h" 13 14 #include "gc/GCContext.h" 15 #include "js/Utility.h" 16 #include "threading/ProtectedData.h" 17 #include "vm/HelperThreads.h" 18 #include "vm/HelperThreadTask.h" 19 20 #define JS_MEMBER_FN_PTR_TYPE(ClassT, ReturnT, /* ArgTs */...) \ 21 ReturnT (ClassT::*)(__VA_ARGS__) 22 23 #define JS_CALL_MEMBER_FN_PTR(Receiver, Ptr, /* Args */...) \ 24 ((Receiver)->*(Ptr))(__VA_ARGS__) 25 26 namespace js { 27 28 namespace gcstats { 29 enum class PhaseKind : uint8_t; 30 } 31 32 namespace gc { 33 34 class GCRuntime; 35 36 static constexpr size_t MaxParallelWorkers = 8; 37 38 static inline mozilla::TimeDuration TimeSince(mozilla::TimeStamp prev) { 39 mozilla::TimeStamp now = mozilla::TimeStamp::Now(); 40 // Sadly this happens sometimes. 41 MOZ_ASSERT(now >= prev); 42 if (now < prev) { 43 now = prev; 44 } 45 return now - prev; 46 } 47 48 } // namespace gc 49 50 class AutoLockHelperThreadState; 51 class GCParallelTask; 52 class HelperThread; 53 54 // A wrapper around a linked list to enforce synchronization. 55 class GCParallelTaskList { 56 mozilla::LinkedList<GCParallelTask> tasks; 57 58 public: 59 bool isEmpty(const AutoLockHelperThreadState& lock) { 60 gHelperThreadLock.assertOwnedByCurrentThread(); 61 return tasks.isEmpty(); 62 } 63 64 void insertBack(GCParallelTask* task, const AutoLockHelperThreadState& lock) { 65 gHelperThreadLock.assertOwnedByCurrentThread(); 66 tasks.insertBack(task); 67 } 68 69 GCParallelTask* popFirst(const AutoLockHelperThreadState& lock) { 70 gHelperThreadLock.assertOwnedByCurrentThread(); 71 return tasks.popFirst(); 72 } 73 74 size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, 75 const AutoLockHelperThreadState& lock) const { 76 gHelperThreadLock.assertOwnedByCurrentThread(); 77 return tasks.sizeOfExcludingThis(aMallocSizeOf); 78 } 79 }; 80 81 // A generic task used to dispatch work to the helper thread system. 82 // Users override the pure-virtual run() method. 83 class GCParallelTask : private mozilla::LinkedListElement<GCParallelTask>, 84 public HelperThreadTask { 85 friend class mozilla::LinkedList<GCParallelTask>; 86 friend class mozilla::LinkedListElement<GCParallelTask>; 87 88 public: 89 gc::GCRuntime* const gc; 90 91 // This can be PhaseKind::NONE for tasks that take place outside a GC. 92 const gcstats::PhaseKind phaseKind; 93 94 gc::GCUse use; 95 96 private: 97 // The state of the parallel computation. 98 enum class State { 99 // The task is idle. Either start() has not been called or join() has 100 // returned. 101 Idle, 102 103 // The task is waiting in the per-runtime queue. 104 Queued, 105 106 // The task has been started but has not yet begun running on a helper 107 // thread. 108 Dispatched, 109 110 // The task is currently running on a helper thread. 111 Running, 112 113 // The task has finished running but has not yet been joined by the main 114 // thread. 115 Finished 116 }; 117 118 UnprotectedData<State> state_; 119 120 HelperThreadLockData<bool> dispatchedToThreadPool; 121 122 // May be set to the time this task was queued to collect telemetry. 123 mozilla::TimeStamp maybeQueueTime_; 124 125 // Amount of time this task took to execute. 126 MainThreadOrGCTaskData<mozilla::TimeDuration> duration_; 127 128 protected: 129 // A flag to signal a request for early completion of the off-thread task. 130 mozilla::Atomic<bool, mozilla::Relaxed> cancel_; 131 132 public: 133 explicit GCParallelTask(gc::GCRuntime* gc, gcstats::PhaseKind phaseKind, 134 gc::GCUse use = gc::GCUse::Unspecified) 135 : gc(gc), 136 phaseKind(phaseKind), 137 use(use), 138 state_(State::Idle), 139 cancel_(false) {} 140 GCParallelTask(GCParallelTask&& other) noexcept 141 : gc(other.gc), 142 phaseKind(other.phaseKind), 143 use(other.use), 144 state_(other.state_), 145 cancel_(false) {} 146 147 explicit GCParallelTask(const GCParallelTask&) = delete; 148 149 // Derived classes must override this to ensure that join() gets called 150 // before members get destructed. 151 virtual ~GCParallelTask(); 152 153 // Time spent in the most recent invocation of this task. 154 mozilla::TimeDuration duration() const { return duration_; } 155 156 // The simple interface to a parallel task works exactly like pthreads. 157 void start(); 158 void join(mozilla::Maybe<mozilla::TimeStamp> deadline = mozilla::Nothing()); 159 160 // If multiple tasks are to be started or joined at once, it is more 161 // efficient to take the helper thread lock once and use these methods. 162 void startWithLockHeld(AutoLockHelperThreadState& lock); 163 void joinWithLockHeld( 164 AutoLockHelperThreadState& lock, 165 mozilla::Maybe<mozilla::TimeStamp> deadline = mozilla::Nothing()); 166 167 // Instead of dispatching to a helper, run the task on the current thread. 168 void runFromMainThread(); 169 void runFromMainThread(AutoLockHelperThreadState& lock); 170 171 // If the task is not already running, either start it or run it on the main 172 // thread if that fails. 173 void startOrRunIfIdle(AutoLockHelperThreadState& lock); 174 175 // Set the cancel flag and wait for the task to finish. 176 void cancelAndWait(); 177 178 // Report whether the task is idle. This means either before start() has been 179 // called or after join() has been called. 180 bool isIdle() const; 181 bool isIdle(const AutoLockHelperThreadState& lock) const { 182 return state_ == State::Idle; 183 } 184 185 // Report whether the task has been started. This means after start() has been 186 // called but before the task has run to completion. The task may not yet have 187 // started running. 188 bool wasStarted() const; 189 bool wasStarted(const AutoLockHelperThreadState& lock) const { 190 return isDispatched(lock) || isRunning(lock); 191 } 192 193 bool isQueued(const AutoLockHelperThreadState& lock) const { 194 return state_ == State::Queued; 195 } 196 197 bool isDispatched(const AutoLockHelperThreadState& lock) const { 198 return state_ == State::Dispatched; 199 } 200 201 bool isNotYetRunning(const AutoLockHelperThreadState& lock) const { 202 return state_ == State::Idle || state_ == State::Queued || 203 state_ == State::Dispatched; 204 } 205 206 const char* getName() override { return "GCParallelTask"; } 207 208 protected: 209 // Override this method to provide the task's functionality. 210 virtual void run(AutoLockHelperThreadState& lock) = 0; 211 212 virtual void recordDuration(); 213 214 bool isCancelled() const { return cancel_; } 215 216 private: 217 void assertIdle() const { 218 // Don't lock here because that adds extra synchronization in debug 219 // builds that may hide bugs. There's no race if the assertion passes. 220 MOZ_ASSERT(state_ == State::Idle); 221 } 222 223 bool isRunning(const AutoLockHelperThreadState& lock) const { 224 return state_ == State::Running; 225 } 226 bool isFinished(const AutoLockHelperThreadState& lock) const { 227 return state_ == State::Finished; 228 } 229 230 void setQueued(const AutoLockHelperThreadState& lock) { 231 MOZ_ASSERT(isIdle(lock)); 232 state_ = State::Queued; 233 } 234 void setDispatched(const AutoLockHelperThreadState& lock) { 235 MOZ_ASSERT(isIdle(lock) || isQueued(lock)); 236 state_ = State::Dispatched; 237 } 238 void setRunning(const AutoLockHelperThreadState& lock) { 239 MOZ_ASSERT(isNotYetRunning(lock)); 240 state_ = State::Running; 241 } 242 void setFinished(const AutoLockHelperThreadState& lock) { 243 MOZ_ASSERT(isRunning(lock)); 244 state_ = State::Finished; 245 } 246 void setIdle(const AutoLockHelperThreadState& lock) { 247 MOZ_ASSERT(!isRunning(lock)); 248 state_ = State::Idle; 249 } 250 friend class gc::GCRuntime; 251 252 void joinNonIdleTask(mozilla::Maybe<mozilla::TimeStamp> deadline, 253 AutoLockHelperThreadState& lock); 254 255 void runTask(JS::GCContext* gcx, AutoLockHelperThreadState& lock); 256 257 // Implement the HelperThreadTask interface. 258 ThreadType threadType() override { 259 return ThreadType::THREAD_TYPE_GCPARALLEL; 260 } 261 void runHelperThreadTask(AutoLockHelperThreadState& locked) override; 262 void onThreadPoolDispatch() override; 263 }; 264 265 } /* namespace js */ 266 #endif /* gc_GCParallelTask_h */