tor-browser

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

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