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/. */
7 #ifndef threading_ProtectedData_h
8 #define threading_ProtectedData_h
10 #include "mozilla/Atomics.h"
12 #include "threading/LockGuard.h"
13 #include "threading/Mutex.h"
14 #include "threading/ThreadId.h"
16 struct JS_PUBLIC_API JSContext
;
20 // This file provides classes for encapsulating pieces of data with a check
21 // that ensures the data is only accessed if certain conditions are met.
22 // Checking is only done in debug builds; in release builds these classes
23 // have no space or time overhead. These classes are mainly used for ensuring
24 // that data is used in threadsafe ways.
26 // ProtectedData does not by itself ensure that data is threadsafe: it only
27 // documents and checks synchronization constraints that need to be established
28 // by the code using the data. If a mutex can be created and directly
29 // associated with the data, consider using the ExclusiveData class instead.
30 // Otherwise, ProtectedData should be used to document whatever synchronization
33 // Protected data checks are enabled in debug builds, except on android where
34 // they cause some permatimeouts in automation.
35 #if defined(DEBUG) && !defined(ANDROID)
36 # define JS_HAS_PROTECTED_DATA_CHECKS
39 #define DECLARE_ONE_BOOL_OPERATOR(OP, T) \
40 template <typename U> \
41 bool operator OP(const U& other) const { \
42 if constexpr (std::is_integral_v<T>) { \
43 return ref() OP static_cast<T>(other); \
45 return ref() OP other; \
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)
57 // Mark a region of code that should be treated as single threaded and suppress
58 // any ProtectedData checks.
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
{
65 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
66 static mozilla::Atomic
<size_t, mozilla::SequentiallyConsistent
> count
;
67 AutoNoteSingleThreadedRegion() { count
++; }
68 ~AutoNoteSingleThreadedRegion() { count
--; }
70 AutoNoteSingleThreadedRegion() {}
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
>
78 typedef ProtectedData
<Check
, T
> ThisType
;
81 template <typename
... Args
>
82 explicit ProtectedData(const Check
& check
, Args
&&... args
)
83 : value(std::forward
<Args
>(args
)...)
84 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
91 DECLARE_BOOL_OPERATORS(T
)
93 operator const T
&() const { return ref(); }
94 const T
& operator->() const { return ref(); }
97 ThisType
& operator=(const U
& p
) {
102 template <typename U
>
103 ThisType
& operator=(U
&& p
) {
104 this->ref() = std::move(p
);
108 template <typename U
>
109 T
& operator+=(const U
& rhs
) {
112 template <typename U
>
113 T
& operator-=(const U
& rhs
) {
116 template <typename U
>
117 T
& operator*=(const U
& rhs
) {
120 template <typename U
>
121 T
& operator/=(const U
& rhs
) {
124 template <typename U
>
125 T
& operator&=(const U
& rhs
) {
128 template <typename U
>
129 T
& operator|=(const U
& rhs
) {
132 T
& operator++() { return ++ref(); }
133 T
& operator--() { return --ref(); }
134 T
operator++(int) { return ref()++; }
135 T
operator--(int) { return ref()--; }
138 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
139 if (!AutoNoteSingleThreadedRegion::count
) {
146 const T
& ref() const {
147 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
148 if (!AutoNoteSingleThreadedRegion::count
) {
155 T
& refNoCheck() { return value
; }
156 const T
& refNoCheck() const { return value
; }
158 static size_t offsetOfValue() { return offsetof(ThisType
, value
); }
162 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
167 // Intermediate class for protected data whose checks take no constructor
169 template <typename Check
, typename T
>
170 class ProtectedDataNoCheckArgs
: public ProtectedData
<Check
, T
> {
171 using Base
= ProtectedData
<Check
, T
>;
174 template <typename
... Args
>
175 explicit ProtectedDataNoCheckArgs(Args
&&... args
)
176 : ProtectedData
<Check
, T
>(Check(), std::forward
<Args
>(args
)...) {}
178 using Base::operator=;
181 // Intermediate class for protected data whose checks take a JSContext.
182 template <typename Check
, typename T
>
183 class ProtectedDataContextArg
: public ProtectedData
<Check
, T
> {
184 using Base
= ProtectedData
<Check
, T
>;
187 template <typename
... Args
>
188 explicit ProtectedDataContextArg(JSContext
* cx
, Args
&&... args
)
189 : ProtectedData
<Check
, T
>(Check(cx
), std::forward
<Args
>(args
)...) {}
191 using Base::operator=;
194 class CheckUnprotected
{
195 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
197 inline void check() const {}
201 // Data with a no-op check that permits all accesses. This is tantamount to not
202 // using ProtectedData at all, but is in place to document points which need
203 // to be fixed in order for runtimes to be multithreaded (see bug 1323066).
204 template <typename T
>
205 using UnprotectedData
= ProtectedDataNoCheckArgs
<CheckUnprotected
, T
>;
207 class CheckThreadLocal
{
208 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
212 CheckThreadLocal() : id(ThreadId::ThisThreadId()) {}
218 class CheckContextLocal
{
219 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
223 explicit CheckContextLocal(JSContext
* cx
) : cx_(cx
) {}
228 explicit CheckContextLocal(JSContext
* cx
) {}
232 // Data which may only be accessed by the thread on which it is created.
233 template <typename T
>
234 using ThreadData
= ProtectedDataNoCheckArgs
<CheckThreadLocal
, T
>;
236 // Data which belongs to a JSContext and should only be accessed from that
237 // JSContext's thread. Note that a JSContext may not have a thread currently
238 // associated with it and any associated thread may change over time.
239 template <typename T
>
240 using ContextData
= ProtectedDataContextArg
<CheckContextLocal
, T
>;
242 // Enum describing which helper threads (GC tasks or Ion compilations) may
244 enum class AllowedHelperThread
{
251 template <AllowedHelperThread Helper
>
252 class CheckMainThread
{
257 // Data which may only be accessed by the runtime's main thread.
258 template <typename T
>
259 using MainThreadData
=
260 ProtectedDataNoCheckArgs
<CheckMainThread
<AllowedHelperThread::None
>, T
>;
262 // Data which may only be accessed by the runtime's main thread or by various
263 // helper thread tasks.
264 template <typename T
>
265 using MainThreadOrGCTaskData
=
266 ProtectedDataNoCheckArgs
<CheckMainThread
<AllowedHelperThread::GCTask
>, T
>;
267 template <typename T
>
268 using MainThreadOrIonCompileData
=
269 ProtectedDataNoCheckArgs
<CheckMainThread
<AllowedHelperThread::IonCompile
>,
271 template <typename T
>
272 using MainThreadOrGCTaskOrIonCompileData
= ProtectedDataNoCheckArgs
<
273 CheckMainThread
<AllowedHelperThread::GCTaskOrIonCompile
>, T
>;
275 // Runtime wide locks which might protect some data.
276 enum class GlobalLock
{ GCLock
, HelperThreadLock
};
278 template <GlobalLock Lock
, AllowedHelperThread Helper
>
279 class CheckGlobalLock
{
280 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
286 // Data which may only be accessed while holding the GC lock.
287 template <typename T
>
288 using GCLockData
= ProtectedDataNoCheckArgs
<
289 CheckGlobalLock
<GlobalLock::GCLock
, AllowedHelperThread::None
>, T
>;
291 // Data which may only be accessed while holding the helper thread lock.
292 template <typename T
>
293 using HelperThreadLockData
= ProtectedDataNoCheckArgs
<
294 CheckGlobalLock
<GlobalLock::HelperThreadLock
, AllowedHelperThread::None
>,
297 // Class for protected data that is only written to once. 'const' may sometimes
298 // be usable instead of this class, but in cases where the data cannot be set
299 // to its final value in its constructor this class is helpful. Protected data
300 // checking only occurs when writes are performed, not reads. Steps may need to
301 // be taken to ensure that reads do not occur until the written value is fully
302 // initialized, as such guarantees are not provided by this class.
303 template <typename Check
, typename T
>
304 class ProtectedDataWriteOnce
{
305 typedef ProtectedDataWriteOnce
<Check
, T
> ThisType
;
308 template <typename
... Args
>
309 explicit ProtectedDataWriteOnce(Args
&&... args
)
310 : value(std::forward
<Args
>(args
)...)
311 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
318 DECLARE_BOOL_OPERATORS(T
)
320 operator const T
&() const { return ref(); }
321 const T
& operator->() const { return ref(); }
323 template <typename U
>
324 ThisType
& operator=(const U
& p
) {
326 this->writeRef() = p
;
331 const T
& ref() const { return value
; }
334 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
335 if (!AutoNoteSingleThreadedRegion::count
) {
338 // Despite the WriteOnce name, actually allow two writes to accommodate
339 // data that is cleared during teardown.
340 MOZ_ASSERT(++nwrites
<= 2);
347 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
353 // Data that is written once with no requirements for exclusive access when
354 // that write occurs.
355 template <typename T
>
356 using WriteOnceData
= ProtectedDataWriteOnce
<CheckUnprotected
, T
>;
358 #undef DECLARE_ASSIGNMENT_OPERATOR
359 #undef DECLARE_ONE_BOOL_OPERATOR
360 #undef DECLARE_BOOL_OPERATORS
364 #endif // threading_ProtectedData_h