ParallelMarking.h (4445B)
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_ParallelMarking_h 8 #define gc_ParallelMarking_h 9 10 #include "mozilla/Atomics.h" 11 #include "mozilla/BitSet.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/TimeStamp.h" 14 15 #include "gc/GCMarker.h" 16 #include "gc/GCParallelTask.h" 17 #include "js/HeapAPI.h" 18 #include "js/SliceBudget.h" 19 #include "threading/ConditionVariable.h" 20 #include "threading/ProtectedData.h" 21 22 namespace js { 23 24 class AutoLockHelperThreadState; 25 26 namespace gc { 27 28 class ParallelMarker; 29 30 using ParallelTaskBitset = mozilla::BitSet<MaxParallelWorkers, uint32_t>; 31 32 // A helper thread task that performs parallel marking. 33 class alignas(TypicalCacheLineSize) ParallelMarkTask : public GCParallelTask { 34 public: 35 friend class ParallelMarker; 36 37 ParallelMarkTask(ParallelMarker* pm, GCMarker* marker, MarkColor color, 38 uint32_t id, const JS::SliceBudget& budget); 39 ~ParallelMarkTask(); 40 41 void run(AutoLockHelperThreadState& lock) override; 42 43 using AtomicCount = mozilla::Atomic<uint32_t, mozilla::Relaxed>; 44 AtomicCount& waitingTaskCountRef(); 45 46 void donateWork(); 47 48 void recordDuration() override; 49 50 private: 51 bool tryMarking(AutoLockHelperThreadState& lock); 52 bool requestWork(AutoLockHelperThreadState& lock); 53 54 void waitUntilResumed(AutoLockHelperThreadState& lock); 55 void resume(); 56 void resumeOnFinish(const AutoLockHelperThreadState& lock); 57 58 bool hasWork() const; 59 60 // The following fields are only accessed by the marker thread: 61 ParallelMarker* const pm; 62 GCMarker* const marker; 63 AutoSetMarkColor color; 64 JS::SliceBudget budget; 65 ConditionVariable resumed; 66 67 const uint32_t id; 68 69 HelperThreadLockData<bool> isWaiting; 70 71 // Length of time this task spent blocked waiting for work. 72 MainThreadOrGCTaskData<mozilla::TimeDuration> markTime; 73 MainThreadOrGCTaskData<mozilla::TimeDuration> waitTime; 74 }; 75 76 // Per-runtime parallel marking state. 77 // 78 // This class is used on the main thread and coordinates parallel marking using 79 // several helper threads running ParallelMarkTasks. 80 // 81 // This uses a work-requesting approach. Threads mark until they run out of 82 // work and then add themselves to a list of waiting tasks and block. Running 83 // tasks with enough work may donate work to a waiting task and resume it. 84 class MOZ_STACK_CLASS ParallelMarker { 85 public: 86 static bool mark(GCRuntime* gc, const JS::SliceBudget& sliceBudget); 87 88 bool hasWaitingTasks() const { return !waitingTasks.IsEmpty(); } 89 90 void donateWorkFrom(GCMarker* src); 91 92 private: 93 static bool markOneColor(GCRuntime* gc, MarkColor color, 94 const JS::SliceBudget& sliceBudget); 95 96 explicit ParallelMarker(GCRuntime* gc, MarkColor color); 97 98 bool mark(const JS::SliceBudget& sliceBudget); 99 100 bool hasWork(MarkColor color) const; 101 102 void addTask(ParallelMarkTask* task, const AutoLockHelperThreadState& lock); 103 104 void addTaskToWaitingList(ParallelMarkTask* task, 105 const AutoLockHelperThreadState& lock); 106 #ifdef DEBUG 107 bool isTaskInWaitingList(const ParallelMarkTask* task, 108 const AutoLockHelperThreadState& lock) const; 109 #endif 110 ParallelMarkTask* takeWaitingTask(); 111 112 bool hasActiveTasks(const AutoLockHelperThreadState& lock) const { 113 return !activeTasks.ref().IsEmpty(); 114 } 115 void setTaskActive(ParallelMarkTask* task, 116 const AutoLockHelperThreadState& lock); 117 void setTaskInactive(ParallelMarkTask* task, 118 const AutoLockHelperThreadState& lock); 119 120 size_t workerCount() const; 121 122 friend class ParallelMarkTask; 123 124 GCRuntime* const gc; 125 126 mozilla::Maybe<ParallelMarkTask> tasks[MaxParallelWorkers]; 127 128 // waitingTasks is written to with the lock held but can be read without. 129 using WaitingTaskSet = 130 mozilla::BitSet<MaxParallelWorkers, 131 mozilla::Atomic<uint32_t, mozilla::Relaxed>>; 132 WaitingTaskSet waitingTasks; 133 134 HelperThreadLockData<ParallelTaskBitset> activeTasks; 135 136 const MarkColor color; 137 }; 138 139 inline ParallelMarkTask::AtomicCount& ParallelMarkTask::waitingTaskCountRef() { 140 return pm->waitingTasks.Storage()[0]; 141 } 142 143 } // namespace gc 144 } // namespace js 145 146 #endif /* gc_ParallelMarking_h */