Line data Source code
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 { return ref() OP static_cast<T>(other); }
36 :
37 : #define DECLARE_BOOL_OPERATORS(T) \
38 : DECLARE_ONE_BOOL_OPERATOR(==, T) \
39 : DECLARE_ONE_BOOL_OPERATOR(!=, 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 :
45 : // Mark a region of code that should be treated as single threaded and suppress
46 : // any ProtectedData checks.
47 : //
48 : // Note that in practice there may be multiple threads running when this class
49 : // is used, due to the presence of multiple runtimes in the process. When each
50 : // process has only a single runtime this will no longer be a concern.
51 : class MOZ_RAII AutoNoteSingleThreadedRegion
52 : {
53 : public:
54 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
55 : static mozilla::Atomic<size_t> count;
56 10 : AutoNoteSingleThreadedRegion() { count++; }
57 10 : ~AutoNoteSingleThreadedRegion() { count--; }
58 : #else
59 : AutoNoteSingleThreadedRegion() {}
60 : #endif
61 : };
62 :
63 : // Class for protected data that may be written to any number of times. Checks
64 : // occur when the data is both read from and written to.
65 : template <typename Check, typename T>
66 0 : class ProtectedData
67 : {
68 : typedef ProtectedData<Check, T> ThisType;
69 :
70 : public:
71 : template <typename... Args>
72 5420 : explicit ProtectedData(const Check& check, Args&&... args)
73 4259 : : value(mozilla::Forward<Args>(args)...)
74 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
75 5420 : , check(check)
76 : #endif
77 5420 : {}
78 :
79 9460240 : DECLARE_BOOL_OPERATORS(T)
80 :
81 79794616 : operator const T&() const { return ref(); }
82 3182277 : const T& operator->() const { return ref(); }
83 :
84 : template <typename U>
85 : ThisType& operator=(const U& p) { this->ref() = p; return *this; }
86 :
87 1010 : template <typename U> T& operator +=(const U& rhs) { return ref() += rhs; }
88 995 : template <typename U> T& operator -=(const U& rhs) { return ref() -= rhs; }
89 0 : template <typename U> T& operator *=(const U& rhs) { return ref() *= rhs; }
90 : template <typename U> T& operator /=(const U& rhs) { return ref() /= rhs; }
91 0 : template <typename U> T& operator &=(const U& rhs) { return ref() &= rhs; }
92 0 : template <typename U> T& operator |=(const U& rhs) { return ref() |= rhs; }
93 44452 : T& operator ++() { return ++ref(); }
94 128060 : T& operator --() { return --ref(); }
95 4332210 : T operator ++(int) { return ref()++; }
96 4246163 : T operator --(int) { return ref()--; }
97 :
98 16175354 : T& ref() {
99 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
100 16175354 : if (!AutoNoteSingleThreadedRegion::count)
101 15024945 : check.check();
102 : #endif
103 16177302 : return value;
104 : }
105 :
106 92459991 : const T& ref() const {
107 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
108 92459991 : if (!AutoNoteSingleThreadedRegion::count)
109 90982859 : check.check();
110 : #endif
111 92479954 : return value;
112 : }
113 :
114 285 : T& refNoCheck() { return value; }
115 60 : const T& refNoCheck() const { return value; }
116 :
117 : private:
118 : T value;
119 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
120 : Check check;
121 : #endif
122 : };
123 :
124 : // Intermediate class for protected data whose checks take no constructor arguments.
125 : template <typename Check, typename T>
126 0 : class ProtectedDataNoCheckArgs : public ProtectedData<Check, T>
127 : {
128 : typedef ProtectedDataNoCheckArgs<Check, T> ThisType;
129 :
130 : public:
131 : template <typename... Args>
132 4023 : explicit ProtectedDataNoCheckArgs(Args&&... args)
133 4023 : : ProtectedData<Check, T>(Check(), mozilla::Forward<Args>(args)...)
134 4023 : {}
135 :
136 : template <typename U>
137 683652 : ThisType& operator=(const U& p) { this->ref() = p; return *this; }
138 : };
139 :
140 : class ZoneGroup;
141 :
142 : // Intermediate class for protected data whose checks take a ZoneGroup constructor argument.
143 : template <typename Check, typename T>
144 0 : class ProtectedDataZoneGroupArg : public ProtectedData<Check, T>
145 : {
146 : typedef ProtectedDataZoneGroupArg<Check, T> ThisType;
147 :
148 : public:
149 : template <typename... Args>
150 1397 : explicit ProtectedDataZoneGroupArg(ZoneGroup* group, Args&&... args)
151 1397 : : ProtectedData<Check, T>(Check(group), mozilla::Forward<Args>(args)...)
152 1397 : {}
153 :
154 : template <typename U>
155 740480 : ThisType& operator=(const U& p) { this->ref() = p; return *this; }
156 : };
157 :
158 : class CheckUnprotected
159 : {
160 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
161 : public:
162 59955459 : inline void check() const {}
163 : #endif
164 : };
165 :
166 : // Data with a no-op check that permits all accesses. This is tantamount to not
167 : // using ProtectedData at all, but is in place to document points which need
168 : // to be fixed in order for runtimes to be multithreaded (see bug 1323066).
169 : template <typename T>
170 : using UnprotectedData = ProtectedDataNoCheckArgs<CheckUnprotected, T>;
171 :
172 : class CheckThreadLocal
173 : {
174 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
175 : Thread::Id id;
176 :
177 : public:
178 2840 : CheckThreadLocal()
179 2840 : : id(ThisThread::GetId())
180 2840 : {}
181 :
182 : void check() const;
183 : #endif
184 : };
185 :
186 : // Data which may only be accessed by the thread on which it is created.
187 : template <typename T>
188 : using ThreadLocalData = ProtectedDataNoCheckArgs<CheckThreadLocal, T>;
189 :
190 : // Enum describing which helper threads (GC tasks or Ion compilations) may
191 : // access data even though they do not have exclusive access to any zone group.
192 : enum class AllowedHelperThread
193 : {
194 : None,
195 : GCTask,
196 : IonCompile,
197 : GCTaskOrIonCompile
198 : };
199 :
200 : template <AllowedHelperThread Helper>
201 : class CheckActiveThread
202 : {
203 : public:
204 : void check() const;
205 : };
206 :
207 : // Data which may only be accessed by the runtime's cooperatively scheduled
208 : // active thread.
209 : template <typename T>
210 : using ActiveThreadData =
211 : ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::None>, T>;
212 :
213 : // Data which may only be accessed by the runtime's cooperatively scheduled
214 : // active thread, or by various helper thread tasks.
215 : template <typename T>
216 : using ActiveThreadOrGCTaskData =
217 : ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::GCTask>, T>;
218 : template <typename T>
219 : using ActiveThreadOrIonCompileData =
220 : ProtectedDataNoCheckArgs<CheckActiveThread<AllowedHelperThread::IonCompile>, T>;
221 :
222 : template <AllowedHelperThread Helper>
223 : class CheckZoneGroup
224 : {
225 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
226 : ZoneGroup* group;
227 :
228 : public:
229 1397 : explicit CheckZoneGroup(ZoneGroup* group) : group(group) {}
230 : void check() const;
231 : #else
232 : public:
233 : explicit CheckZoneGroup(ZoneGroup* group) {}
234 : #endif
235 : };
236 :
237 : // Data which may only be accessed by threads with exclusive access to the
238 : // associated zone group, or by the runtime's cooperatively scheduled
239 : // active thread for zone groups which are not in use by a helper thread.
240 : template <typename T>
241 : using ZoneGroupData =
242 : ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::None>, T>;
243 :
244 : // Data which may only be accessed by threads with exclusive access to the
245 : // associated zone group, or by various helper thread tasks.
246 : template <typename T>
247 : using ZoneGroupOrGCTaskData =
248 : ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::GCTask>, T>;
249 : template <typename T>
250 : using ZoneGroupOrIonCompileData =
251 : ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::IonCompile>, T>;
252 : template <typename T>
253 : using ZoneGroupOrGCTaskOrIonCompileData =
254 : ProtectedDataZoneGroupArg<CheckZoneGroup<AllowedHelperThread::GCTaskOrIonCompile>, T>;
255 :
256 : // Runtime wide locks which might protect some data.
257 : enum class GlobalLock
258 : {
259 : GCLock,
260 : ExclusiveAccessLock,
261 : HelperThreadLock
262 : };
263 :
264 : template <GlobalLock Lock, AllowedHelperThread Helper>
265 : class CheckGlobalLock
266 : {
267 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
268 : public:
269 : void check() const;
270 : #endif
271 : };
272 :
273 : // Data which may only be accessed while holding the GC lock.
274 : template <typename T>
275 : using GCLockData =
276 : ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>, T>;
277 :
278 : // Data which may only be accessed while holding the exclusive access lock.
279 : template <typename T>
280 : using ExclusiveAccessLockData =
281 : ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>, T>;
282 :
283 : // Data which may only be accessed while holding the exclusive access lock or
284 : // by GC helper thread tasks (at which point a foreground thread should be
285 : // holding the exclusive access lock, though we do not check this).
286 : template <typename T>
287 : using ExclusiveAccessLockOrGCTaskData =
288 : ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::GCTask>, T>;
289 :
290 : // Data which may only be accessed while holding the helper thread lock.
291 : template <typename T>
292 : using HelperThreadLockData =
293 : ProtectedDataNoCheckArgs<CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>, T>;
294 :
295 : // Class for protected data that is only written to once. 'const' may sometimes
296 : // be usable instead of this class, but in cases where the data cannot be set
297 : // to its final value in its constructor this class is helpful. Protected data
298 : // checking only occurs when writes are performed, not reads. Steps may need to
299 : // be taken to ensure that reads do not occur until the written value is fully
300 : // initialized, as such guarantees are not provided by this class.
301 : template <typename Check, typename T>
302 0 : class ProtectedDataWriteOnce
303 : {
304 : typedef ProtectedDataWriteOnce<Check, T> ThisType;
305 :
306 : public:
307 : template <typename... Args>
308 232 : explicit ProtectedDataWriteOnce(Args&&... args)
309 188 : : value(mozilla::Forward<Args>(args)...)
310 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
311 232 : , nwrites(0)
312 : #endif
313 232 : {}
314 :
315 35908619 : DECLARE_BOOL_OPERATORS(T)
316 :
317 7950228 : operator const T&() const { return ref(); }
318 29956 : const T& operator->() const { return ref(); }
319 :
320 : template <typename U>
321 165 : ThisType& operator=(const U& p) {
322 165 : if (ref() != p)
323 129 : this->writeRef() = p;
324 165 : return *this;
325 : }
326 :
327 43912258 : const T& ref() const { return value; }
328 :
329 137 : T& writeRef() {
330 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
331 137 : if (!AutoNoteSingleThreadedRegion::count)
332 22 : check.check();
333 : // Despite the WriteOnce name, actually allow two writes to accommodate
334 : // data that is cleared during teardown.
335 137 : MOZ_ASSERT(++nwrites <= 2);
336 : #endif
337 137 : return value;
338 : }
339 :
340 : private:
341 : T value;
342 : #ifdef JS_HAS_PROTECTED_DATA_CHECKS
343 : Check check;
344 : size_t nwrites;
345 : #endif
346 : };
347 :
348 : // Data that is written once with no requirements for exclusive access when
349 : // that write occurs.
350 : template <typename T>
351 : using WriteOnceData = ProtectedDataWriteOnce<CheckUnprotected, T>;
352 :
353 : // Data that is written once, and only while holding the exclusive access lock.
354 : template <typename T>
355 : using ExclusiveAccessLockWriteOnceData =
356 : ProtectedDataWriteOnce<CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>, T>;
357 :
358 : #undef DECLARE_ASSIGNMENT_OPERATOR
359 : #undef DECLARE_ONE_BOOL_OPERATOR
360 : #undef DECLARE_BOOL_OPERATORS
361 :
362 : } // namespace js
363 :
364 : #endif // threading_ProtectedData_h
|