ParallelWork.h (3897B)
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_ParallelWork_h 8 #define gc_ParallelWork_h 9 10 #include "mozilla/Maybe.h" 11 12 #include <algorithm> 13 14 #include "gc/GCParallelTask.h" 15 #include "gc/GCRuntime.h" 16 #include "js/SliceBudget.h" 17 #include "vm/HelperThreads.h" 18 19 namespace js { 20 21 namespace gcstats { 22 enum class PhaseKind : uint8_t; 23 } 24 25 namespace gc { 26 27 template <typename WorkItem> 28 using ParallelWorkFunc = size_t (*)(GCRuntime*, const WorkItem&); 29 30 // A GCParallelTask task that executes WorkItems from a WorkItemIterator. 31 // 32 // The WorkItemIterator class must supply done(), next() and get() methods. The 33 // get() method must return WorkItems objects. 34 template <typename WorkItem, typename WorkItemIterator> 35 class ParallelWorker : public GCParallelTask { 36 public: 37 using WorkFunc = ParallelWorkFunc<WorkItem>; 38 39 ParallelWorker(GCRuntime* gc, gcstats::PhaseKind phaseKind, GCUse use, 40 WorkFunc func, WorkItemIterator& work, 41 const JS::SliceBudget& budget, AutoLockHelperThreadState& lock) 42 : GCParallelTask(gc, phaseKind, use), 43 func_(func), 44 work_(work), 45 budget_(budget), 46 item_(work.get()) { 47 // Consume a work item on creation so that we can stop creating workers if 48 // the number of workers exceeds the number of work items. 49 work.next(); 50 } 51 52 void run(AutoLockHelperThreadState& lock) { 53 AutoUnlockHelperThreadState unlock(lock); 54 55 for (;;) { 56 size_t steps = func_(gc, item_); 57 budget_.step(std::max(steps, size_t(1))); 58 if (budget_.isOverBudget()) { 59 break; 60 } 61 62 AutoLockHelperThreadState lock; 63 if (work().done()) { 64 break; 65 } 66 67 item_ = work().get(); 68 work().next(); 69 } 70 } 71 72 private: 73 WorkItemIterator& work() { return work_.ref(); } 74 75 // A function to execute work items on the helper thread. 76 WorkFunc func_; 77 78 // An iterator which produces work items to execute. 79 HelperThreadLockData<WorkItemIterator&> work_; 80 81 // The budget that determines how long to run for. 82 JS::SliceBudget budget_; 83 84 // The next work item to process. 85 WorkItem item_; 86 }; 87 88 // An RAII class that starts a number of ParallelWorkers and waits for them to 89 // finish. 90 template <typename WorkItem, typename WorkItemIterator> 91 class MOZ_RAII AutoRunParallelWork { 92 public: 93 using Worker = ParallelWorker<WorkItem, WorkItemIterator>; 94 using WorkFunc = ParallelWorkFunc<WorkItem>; 95 96 AutoRunParallelWork(GCRuntime* gc, WorkFunc func, 97 gcstats::PhaseKind phaseKind, GCUse use, 98 WorkItemIterator& work, const JS::SliceBudget& budget, 99 AutoLockHelperThreadState& lock) 100 : gc(gc), phaseKind(phaseKind), lock(lock), tasksStarted(0) { 101 size_t workerCount = gc->parallelWorkerCount(); 102 MOZ_ASSERT(workerCount <= MaxParallelWorkers); 103 MOZ_ASSERT_IF(workerCount == 0, work.done()); 104 105 for (size_t i = 0; i < workerCount && !work.done(); i++) { 106 tasks[i].emplace(gc, phaseKind, use, func, work, budget, lock); 107 gc->startTask(*tasks[i], lock); 108 tasksStarted++; 109 } 110 } 111 112 ~AutoRunParallelWork() { 113 gHelperThreadLock.assertOwnedByCurrentThread(); 114 115 for (size_t i = 0; i < tasksStarted; i++) { 116 gc->joinTask(*tasks[i], lock); 117 } 118 for (size_t i = tasksStarted; i < MaxParallelWorkers; i++) { 119 MOZ_ASSERT(tasks[i].isNothing()); 120 } 121 } 122 123 private: 124 GCRuntime* gc; 125 gcstats::PhaseKind phaseKind; 126 AutoLockHelperThreadState& lock; 127 size_t tasksStarted; 128 mozilla::Maybe<Worker> tasks[MaxParallelWorkers]; 129 }; 130 131 } /* namespace gc */ 132 } /* namespace js */ 133 134 #endif /* gc_ParallelWork_h */