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