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