tor-browser

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

AtomicsObject.h (6999B)


      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 builtin_AtomicsObject_h
      8 #define builtin_AtomicsObject_h
      9 
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/TimeStamp.h"
     12 
     13 #include "threading/ConditionVariable.h"
     14 #include "threading/ProtectedData.h"  // js::ThreadData
     15 #include "vm/NativeObject.h"
     16 #include "vm/PlainObject.h"
     17 
     18 namespace js {
     19 
     20 class SharedArrayRawBuffer;
     21 
     22 class AtomicsObject : public NativeObject {
     23 public:
     24  static const JSClass class_;
     25 };
     26 
     27 enum class FutexWaiterKind { Sync, Async, ListHead };
     28 
     29 class FutexWaiter;
     30 class SyncFutexWaiter;
     31 class AsyncFutexWaiter;
     32 
     33 class FutexWaiterListNode {
     34 private:
     35  FutexWaiterListNode* next_ = nullptr;  // Lower priority node in waiter list
     36  FutexWaiterListNode* prev_ = nullptr;  // Higher priority node in waiter list
     37 
     38 protected:
     39  explicit FutexWaiterListNode(FutexWaiterKind kind) : kind_(kind) {}
     40  FutexWaiterKind kind_;
     41 
     42 public:
     43  FutexWaiter* toWaiter() {
     44    MOZ_ASSERT(kind_ != FutexWaiterKind::ListHead);
     45    return reinterpret_cast<FutexWaiter*>(this);
     46  }
     47 
     48  FutexWaiterKind kind() const { return kind_; }
     49 
     50  FutexWaiterListNode* next() { return next_; }
     51  void setNext(FutexWaiterListNode* next) { next_ = next; }
     52  FutexWaiterListNode* prev() { return prev_; }
     53  void setPrev(FutexWaiterListNode* prev) { prev_ = prev; }
     54 };
     55 
     56 class FutexWaiterListHead : public FutexWaiterListNode {
     57 public:
     58  FutexWaiterListHead() : FutexWaiterListNode(FutexWaiterKind::ListHead) {
     59    setNext(this);
     60    setPrev(this);
     61  }
     62  ~FutexWaiterListHead();
     63 };
     64 
     65 class FutexThread {
     66  friend class AutoLockFutexAPI;
     67 
     68 public:
     69  [[nodiscard]] static bool initialize();
     70  static void destroy();
     71 
     72  static void lock();
     73  static void unlock();
     74 
     75  FutexThread();
     76  [[nodiscard]] bool initInstance();
     77  void destroyInstance();
     78 
     79  // Parameters to notify().
     80  enum NotifyReason {
     81    NotifyExplicit,       // Being asked to wake up by another thread
     82    NotifyForJSInterrupt  // Interrupt requested
     83  };
     84 
     85  // Result codes from wait() and atomics_wait_impl().
     86  enum class WaitResult {
     87    Error,     // Error has been reported, just propagate error signal
     88    NotEqual,  // Did not wait because the values differed
     89    OK,        // Waited and was woken
     90    TimedOut   // Waited and timed out
     91  };
     92 
     93  // Block the calling thread and wait.
     94  //
     95  // The futex lock must be held around this call.
     96  //
     97  // The timeout is the number of milliseconds, with fractional
     98  // times allowed; specify mozilla::Nothing() for an indefinite
     99  // wait.
    100  //
    101  // wait() will not wake up spuriously.
    102  [[nodiscard]] WaitResult wait(
    103      JSContext* cx, js::UniqueLock<js::Mutex>& locked,
    104      const mozilla::Maybe<mozilla::TimeDuration>& timeout);
    105 
    106  // Notify the thread this is associated with.
    107  //
    108  // The futex lock must be held around this call.  (The sleeping
    109  // thread will not wake up until the caller of Atomics.notify()
    110  // releases the lock.)
    111  //
    112  // If the thread is not waiting then this method does nothing.
    113  //
    114  // If the thread is waiting in a call to wait() and the
    115  // reason is NotifyExplicit then the wait() call will return
    116  // with Woken.
    117  //
    118  // If the thread is waiting in a call to wait() and the
    119  // reason is NotifyForJSInterrupt then the wait() will return
    120  // with WaitingNotifiedForInterrupt; in the latter case the caller
    121  // of wait() must handle the interrupt.
    122  void notify(NotifyReason reason);
    123 
    124  bool isWaiting();
    125 
    126  // If canWait() returns false (the default) then wait() is disabled
    127  // on the thread to which the FutexThread belongs.
    128  bool canWait() { return canWait_; }
    129 
    130  void setCanWait(bool flag) { canWait_ = flag; }
    131 
    132 private:
    133  enum FutexState {
    134    Idle,                         // We are not waiting or woken
    135    Waiting,                      // We are waiting, nothing has happened yet
    136    WaitingNotifiedForInterrupt,  // We are waiting, but have been interrupted,
    137                                  //   and have not yet started running the
    138                                  //   interrupt handler
    139    WaitingInterrupted,           // We are waiting, but have been interrupted
    140                                  //   and are running the interrupt handler
    141    Woken                         // Woken by a script call to Atomics.notify
    142  };
    143 
    144  // Condition variable that this runtime will wait on.
    145  js::ConditionVariable* cond_;
    146 
    147  // Current futex state for this runtime.  When not in a wait this
    148  // is Idle; when in a wait it is Waiting or the reason the futex
    149  // is about to wake up.
    150  FutexState state_;
    151 
    152  // Shared futex lock for all runtimes.  We can perhaps do better,
    153  // but any lock will need to be per-domain (consider SharedWorker)
    154  // or coarser.
    155  static mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent> lock_;
    156 
    157  // A flag that controls whether waiting is allowed.
    158  ThreadData<bool> canWait_;
    159 };
    160 
    161 // Go to sleep if the int32_t value at the given address equals `value`.
    162 [[nodiscard]] FutexThread::WaitResult atomics_wait_impl(
    163    JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value,
    164    const mozilla::Maybe<mozilla::TimeDuration>& timeout);
    165 
    166 // Go to sleep if the int64_t value at the given address equals `value`.
    167 [[nodiscard]] FutexThread::WaitResult atomics_wait_impl(
    168    JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value,
    169    const mozilla::Maybe<mozilla::TimeDuration>& timeout);
    170 
    171 // If the int32_t value at the given address equals `value`, return a result
    172 // object containing a promise that will be resolved when that address is
    173 // notified.
    174 [[nodiscard]] PlainObject* atomics_wait_async_impl(
    175    JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value,
    176    const mozilla::Maybe<mozilla::TimeDuration>& timeout);
    177 
    178 // If the int64_t value at the given address equals `value`, return a result
    179 // object containing a promise that will be resolved when that address is
    180 // notified.
    181 [[nodiscard]] PlainObject* atomics_wait_async_impl(
    182    JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value,
    183    const mozilla::Maybe<mozilla::TimeDuration>& timeout);
    184 
    185 // Notify some waiters on the given address.  If `count` is negative then notify
    186 // all.  The return value is nonnegative and is the number of waiters woken.  If
    187 // the number of waiters woken exceeds INT64_MAX then this never returns.  If
    188 // `count` is nonnegative then the woken out parameter is never greater than
    189 // `count`.
    190 [[nodiscard]] bool atomics_notify_impl(JSContext* cx,
    191                                       SharedArrayRawBuffer* sarb,
    192                                       size_t byteOffset, int64_t count,
    193                                       int64_t* woken);
    194 
    195 } /* namespace js */
    196 
    197 #endif /* builtin_AtomicsObject_h */