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