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