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