tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */