diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/gc/ParallelWork.h | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | js/src/gc/ParallelWork.h | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/js/src/gc/ParallelWork.h b/js/src/gc/ParallelWork.h new file mode 100644 index 0000000000..9de9561c35 --- /dev/null +++ b/js/src/gc/ParallelWork.h @@ -0,0 +1,139 @@ +/* -*- 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_ParallelWork_h +#define gc_ParallelWork_h + +#include "mozilla/Maybe.h" + +#include "gc/GC.h" +#include "gc/GCParallelTask.h" +#include "gc/GCRuntime.h" +#include "js/SliceBudget.h" +#include "vm/HelperThreads.h" + +namespace js { + +namespace gcstats { +enum class PhaseKind : uint8_t; +} + +namespace gc { + +template <typename WorkItem> +using ParallelWorkFunc = size_t (*)(GCRuntime*, const WorkItem&); + +// A GCParallelTask task that executes WorkItems from a WorkItemIterator. +// +// The WorkItemIterator class must supply done(), next() and get() methods. The +// get() method must return WorkItems objects. +template <typename WorkItem, typename WorkItemIterator> +class ParallelWorker : public GCParallelTask { + public: + using WorkFunc = ParallelWorkFunc<WorkItem>; + + ParallelWorker(GCRuntime* gc, WorkFunc func, WorkItemIterator& work, + const SliceBudget& budget, AutoLockHelperThreadState& lock) + : GCParallelTask(gc), + func_(func), + work_(work), + budget_(budget), + item_(work.get()) { + // Consume a work item on creation so that we can stop creating workers if + // the number of workers exceeds the number of work items. + work.next(); + } + + void run(AutoLockHelperThreadState& lock) { + AutoUnlockHelperThreadState unlock(lock); + + // These checks assert when run in parallel. + AutoDisableProxyCheck noProxyCheck; + + for (;;) { + size_t steps = func_(gc, item_); + budget_.step(steps); + if (budget_.isOverBudget()) { + break; + } + + AutoLockHelperThreadState lock; + if (work().done()) { + break; + } + + item_ = work().get(); + work().next(); + } + } + + private: + WorkItemIterator& work() { return work_.ref(); } + + // A function to execute work items on the helper thread. + WorkFunc func_; + + // An iterator which produces work items to execute. + HelperThreadLockData<WorkItemIterator&> work_; + + // The budget that determines how long to run for. + SliceBudget budget_; + + // The next work item to process. + WorkItem item_; +}; + +static constexpr size_t MaxParallelWorkers = 8; + +extern size_t ParallelWorkerCount(); + +// An RAII class that starts a number of ParallelWorkers and waits for them to +// finish. +template <typename WorkItem, typename WorkItemIterator> +class MOZ_RAII AutoRunParallelWork { + public: + using Worker = ParallelWorker<WorkItem, WorkItemIterator>; + using WorkFunc = ParallelWorkFunc<WorkItem>; + + AutoRunParallelWork(GCRuntime* gc, WorkFunc func, + gcstats::PhaseKind phaseKind, WorkItemIterator& work, + const SliceBudget& budget, + AutoLockHelperThreadState& lock) + : gc(gc), phaseKind(phaseKind), lock(lock), tasksStarted(0) { + size_t workerCount = gc->parallelWorkerCount(); + MOZ_ASSERT(workerCount <= MaxParallelWorkers); + MOZ_ASSERT_IF(workerCount == 0, work.done()); + + for (size_t i = 0; i < workerCount && !work.done(); i++) { + tasks[i].emplace(gc, func, work, budget, lock); + gc->startTask(*tasks[i], phaseKind, lock); + tasksStarted++; + } + } + + ~AutoRunParallelWork() { + gHelperThreadLock.assertOwnedByCurrentThread(); + + for (size_t i = 0; i < tasksStarted; i++) { + gc->joinTask(*tasks[i], phaseKind, lock); + } + for (size_t i = tasksStarted; i < MaxParallelWorkers; i++) { + MOZ_ASSERT(tasks[i].isNothing()); + } + } + + private: + GCRuntime* gc; + gcstats::PhaseKind phaseKind; + AutoLockHelperThreadState& lock; + size_t tasksStarted; + mozilla::Maybe<Worker> tasks[MaxParallelWorkers]; +}; + +} /* namespace gc */ +} /* namespace js */ + +#endif /* gc_ParallelWork_h */ |