tor-browser

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

ProtectedData.h (11218B)


      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 threading_ProtectedData_h
      8 #define threading_ProtectedData_h
      9 
     10 #include "mozilla/Atomics.h"
     11 #include <utility>
     12 #include "jstypes.h"
     13 #include "threading/ThreadId.h"
     14 
     15 struct JS_PUBLIC_API JSContext;
     16 
     17 namespace js {
     18 
     19 class Mutex;
     20 
     21 // This file provides classes for encapsulating pieces of data with a check
     22 // that ensures the data is only accessed if certain conditions are met.
     23 // Checking is only done in debug builds; in release builds these classes
     24 // have no space or time overhead. These classes are mainly used for ensuring
     25 // that data is used in threadsafe ways.
     26 //
     27 // ProtectedData does not by itself ensure that data is threadsafe: it only
     28 // documents and checks synchronization constraints that need to be established
     29 // by the code using the data. If a mutex can be created and directly
     30 // associated with the data, consider using the ExclusiveData class instead.
     31 // Otherwise, ProtectedData should be used to document whatever synchronization
     32 // method is used.
     33 
     34 // Protected data checks are enabled in debug builds, except on android where
     35 // they cause some permatimeouts in automation.
     36 #if defined(DEBUG) && !defined(ANDROID)
     37 #  define JS_HAS_PROTECTED_DATA_CHECKS
     38 #endif
     39 
     40 #define DECLARE_ONE_BOOL_OPERATOR(OP, T)     \
     41  template <typename U>                      \
     42  bool operator OP(const U& other) const {   \
     43    if constexpr (std::is_integral_v<T>) {   \
     44      return ref() OP static_cast<T>(other); \
     45    } else {                                 \
     46      return ref() OP other;                 \
     47    }                                        \
     48  }
     49 
     50 #define DECLARE_BOOL_OPERATORS(T)  \
     51  DECLARE_ONE_BOOL_OPERATOR(==, T) \
     52  DECLARE_ONE_BOOL_OPERATOR(!=, T) \
     53  DECLARE_ONE_BOOL_OPERATOR(<=, T) \
     54  DECLARE_ONE_BOOL_OPERATOR(>=, T) \
     55  DECLARE_ONE_BOOL_OPERATOR(<, T)  \
     56  DECLARE_ONE_BOOL_OPERATOR(>, T)
     57 
     58 // Mark a region of code that should be treated as single threaded and suppress
     59 // any ProtectedData checks.
     60 //
     61 // Note that in practice there may be multiple threads running when this class
     62 // is used, due to the presence of multiple runtimes in the process. When each
     63 // process has only a single runtime this will no longer be a concern.
     64 class MOZ_RAII AutoNoteSingleThreadedRegion {
     65 public:
     66 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
     67  static mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> count;
     68  AutoNoteSingleThreadedRegion() { count++; }
     69  ~AutoNoteSingleThreadedRegion() { count--; }
     70 #else
     71  AutoNoteSingleThreadedRegion() {}
     72 #endif
     73 };
     74 
     75 // Class for protected data that may be written to any number of times. Checks
     76 // occur when the data is both read from and written to.
     77 template <typename Check, typename T>
     78 class ProtectedData {
     79  using ThisType = ProtectedData<Check, T>;
     80 
     81 public:
     82  template <typename... Args>
     83  explicit ProtectedData(const Check& check, Args&&... args)
     84      : value(std::forward<Args>(args)...)
     85 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
     86        ,
     87        check(check)
     88 #endif
     89  {
     90  }
     91 
     92  DECLARE_BOOL_OPERATORS(T)
     93 
     94  operator const T&() const { return ref(); }
     95  const T& operator->() const { return ref(); }
     96 
     97  template <typename U>
     98  ThisType& operator=(const U& p) {
     99    this->ref() = p;
    100    return *this;
    101  }
    102 
    103  template <typename U>
    104  ThisType& operator=(U&& p) {
    105    this->ref() = std::move(p);
    106    return *this;
    107  }
    108 
    109  template <typename U>
    110  T& operator+=(const U& rhs) {
    111    return ref() += rhs;
    112  }
    113  template <typename U>
    114  T& operator-=(const U& rhs) {
    115    return ref() -= rhs;
    116  }
    117  template <typename U>
    118  T& operator*=(const U& rhs) {
    119    return ref() *= rhs;
    120  }
    121  template <typename U>
    122  T& operator/=(const U& rhs) {
    123    return ref() /= rhs;
    124  }
    125  template <typename U>
    126  T& operator&=(const U& rhs) {
    127    return ref() &= rhs;
    128  }
    129  template <typename U>
    130  T& operator|=(const U& rhs) {
    131    return ref() |= rhs;
    132  }
    133  T& operator++() { return ++ref(); }
    134  T& operator--() { return --ref(); }
    135  T operator++(int) { return ref()++; }
    136  T operator--(int) { return ref()--; }
    137 
    138  T& ref() {
    139 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    140    if (!AutoNoteSingleThreadedRegion::count) {
    141      check.check();
    142    }
    143 #endif
    144    return value;
    145  }
    146 
    147  const T& ref() const {
    148 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    149    if (!AutoNoteSingleThreadedRegion::count) {
    150      check.check();
    151    }
    152 #endif
    153    return value;
    154  }
    155 
    156  T& refNoCheck() { return value; }
    157  const T& refNoCheck() const { return value; }
    158 
    159  static constexpr size_t offsetOfValue() { return offsetof(ThisType, value); }
    160 
    161 private:
    162  T value;
    163 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    164  Check check;
    165 #endif
    166 };
    167 
    168 // Intermediate class for protected data whose checks take no constructor
    169 // arguments.
    170 template <typename Check, typename T>
    171 class ProtectedDataNoCheckArgs : public ProtectedData<Check, T> {
    172  using Base = ProtectedData<Check, T>;
    173 
    174 public:
    175  template <typename... Args>
    176  explicit ProtectedDataNoCheckArgs(Args&&... args)
    177      : ProtectedData<Check, T>(Check(), std::forward<Args>(args)...) {}
    178 
    179  using Base::operator=;
    180 };
    181 
    182 // Intermediate class for protected data whose checks take a single argument.
    183 template <typename CheckArg, typename Check, typename T>
    184 class ProtectedDataWithArg : public ProtectedData<Check, T> {
    185  using Base = ProtectedData<Check, T>;
    186 
    187 public:
    188  template <typename... Args>
    189  explicit ProtectedDataWithArg(CheckArg checkArg, Args&&... args)
    190      : ProtectedData<Check, T>(Check(checkArg), std::forward<Args>(args)...) {}
    191 
    192  using Base::operator=;
    193 };
    194 
    195 template <typename Check, typename T>
    196 using ProtectedDataContextArg = ProtectedDataWithArg<JSContext*, Check, T>;
    197 
    198 template <typename Check, typename T>
    199 using ProtectedDataMutexArg = ProtectedDataWithArg<const Mutex&, Check, T>;
    200 
    201 class CheckUnprotected {
    202 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    203 public:
    204  inline void check() const {}
    205 #endif
    206 };
    207 
    208 // Data with a no-op check that permits all accesses. This is tantamount to not
    209 // using ProtectedData at all, but is in place to document points which need
    210 // to be fixed in order for runtimes to be multithreaded (see bug 1323066).
    211 template <typename T>
    212 using UnprotectedData = ProtectedDataNoCheckArgs<CheckUnprotected, T>;
    213 
    214 class CheckThreadLocal {
    215 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    216  ThreadId id;
    217 
    218 public:
    219  CheckThreadLocal() : id(ThreadId::ThisThreadId()) {}
    220 
    221  void check() const;
    222 #endif
    223 };
    224 
    225 class CheckContextLocal {
    226 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    227  JSContext* cx_;
    228 
    229 public:
    230  explicit CheckContextLocal(JSContext* cx) : cx_(cx) {}
    231 
    232  void check() const;
    233 #else
    234 public:
    235  explicit CheckContextLocal(JSContext* cx) {}
    236 #endif
    237 };
    238 
    239 class CheckMutexHeld {
    240 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    241  const Mutex& mutex_;
    242 
    243 public:
    244  explicit CheckMutexHeld(const Mutex& mutex) : mutex_(mutex) {}
    245 
    246  void check() const;
    247 #else
    248 public:
    249  explicit CheckMutexHeld(const Mutex& mutex) {}
    250 #endif
    251 };
    252 
    253 // Data which may only be accessed by the thread on which it is created.
    254 template <typename T>
    255 using ThreadData = ProtectedDataNoCheckArgs<CheckThreadLocal, T>;
    256 
    257 // Data which belongs to a JSContext and should only be accessed from that
    258 // JSContext's thread. Note that a JSContext may not have a thread currently
    259 // associated with it and any associated thread may change over time.
    260 template <typename T>
    261 using ContextData = ProtectedDataContextArg<CheckContextLocal, T>;
    262 
    263 template <typename T>
    264 using MutexData = ProtectedDataMutexArg<CheckMutexHeld, T>;
    265 
    266 // Enum describing which helper threads (GC tasks or Ion compilations) may
    267 // access data.
    268 enum class AllowedHelperThread {
    269  None,
    270  GCTask,
    271  IonCompile,
    272  GCTaskOrIonCompile,
    273 };
    274 
    275 template <AllowedHelperThread Helper>
    276 class CheckMainThread {
    277 public:
    278  void check() const;
    279 };
    280 
    281 // Data which may only be accessed by the runtime's main thread.
    282 template <typename T>
    283 using MainThreadData =
    284    ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::None>, T>;
    285 
    286 // Data which may only be accessed by the runtime's main thread or by various
    287 // helper thread tasks.
    288 template <typename T>
    289 using MainThreadOrGCTaskData =
    290    ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::GCTask>, T>;
    291 template <typename T>
    292 using MainThreadOrIonCompileData =
    293    ProtectedDataNoCheckArgs<CheckMainThread<AllowedHelperThread::IonCompile>,
    294                             T>;
    295 template <typename T>
    296 using MainThreadOrGCTaskOrIonCompileData = ProtectedDataNoCheckArgs<
    297    CheckMainThread<AllowedHelperThread::GCTaskOrIonCompile>, T>;
    298 
    299 // Runtime wide locks which might protect some data.
    300 enum class GlobalLock { GCLock, HelperThreadLock };
    301 
    302 template <GlobalLock Lock, AllowedHelperThread Helper>
    303 class CheckGlobalLock {
    304 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    305 public:
    306  void check() const;
    307 #endif
    308 };
    309 
    310 // Data which may only be accessed while holding the GC lock.
    311 template <typename T>
    312 using GCLockData = ProtectedDataNoCheckArgs<
    313    CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>, T>;
    314 
    315 // Data which may only be accessed while holding the helper thread lock.
    316 template <typename T>
    317 using HelperThreadLockData = ProtectedDataNoCheckArgs<
    318    CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>,
    319    T>;
    320 
    321 // Class for protected data that is only written to once. 'const' may sometimes
    322 // be usable instead of this class, but in cases where the data cannot be set
    323 // to its final value in its constructor this class is helpful. Protected data
    324 // checking only occurs when writes are performed, not reads. Steps may need to
    325 // be taken to ensure that reads do not occur until the written value is fully
    326 // initialized, as such guarantees are not provided by this class.
    327 template <typename Check, typename T>
    328 class ProtectedDataWriteOnce {
    329  using ThisType = ProtectedDataWriteOnce<Check, T>;
    330 
    331 public:
    332  template <typename... Args>
    333  explicit ProtectedDataWriteOnce(Args&&... args)
    334      : value(std::forward<Args>(args)...)
    335 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    336        ,
    337        nwrites(0)
    338 #endif
    339  {
    340  }
    341 
    342  DECLARE_BOOL_OPERATORS(T)
    343 
    344  operator const T&() const { return ref(); }
    345  const T& operator->() const { return ref(); }
    346 
    347  template <typename U>
    348  ThisType& operator=(const U& p) {
    349    if (ref() != p) {
    350      this->writeRef() = p;
    351    }
    352    return *this;
    353  }
    354 
    355  const T& ref() const { return value; }
    356 
    357  T& writeRef() {
    358 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    359    if (!AutoNoteSingleThreadedRegion::count) {
    360      check.check();
    361    }
    362    // Despite the WriteOnce name, actually allow two writes to accommodate
    363    // data that is cleared during teardown.
    364    MOZ_ASSERT(++nwrites <= 2);
    365 #endif
    366    return value;
    367  }
    368 
    369 private:
    370  T value;
    371 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
    372  Check check;
    373  size_t nwrites;
    374 #endif
    375 };
    376 
    377 // Data that is written once with no requirements for exclusive access when
    378 // that write occurs.
    379 template <typename T>
    380 using WriteOnceData = ProtectedDataWriteOnce<CheckUnprotected, T>;
    381 
    382 #undef DECLARE_ASSIGNMENT_OPERATOR
    383 #undef DECLARE_ONE_BOOL_OPERATOR
    384 #undef DECLARE_BOOL_OPERATORS
    385 
    386 }  // namespace js
    387 
    388 #endif  // threading_ProtectedData_h