Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / src / threading / ProtectedData.h
blobbff58a871c6dc607389510f00ad4f91efebbe644
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"
11 #include "jstypes.h"
12 #include "threading/LockGuard.h"
13 #include "threading/Mutex.h"
14 #include "threading/ThreadId.h"
16 struct JS_PUBLIC_API JSContext;
18 namespace js {
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
31 // method is used.
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
37 #endif
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); \
44 } else { \
45 return ref() OP other; \
46 } \
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 {
64 public:
65 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
66 static mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> count;
67 AutoNoteSingleThreadedRegion() { count++; }
68 ~AutoNoteSingleThreadedRegion() { count--; }
69 #else
70 AutoNoteSingleThreadedRegion() {}
71 #endif
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;
80 public:
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
86 check(check)
87 #endif
91 DECLARE_BOOL_OPERATORS(T)
93 operator const T&() const { return ref(); }
94 const T& operator->() const { return ref(); }
96 template <typename U>
97 ThisType& operator=(const U& p) {
98 this->ref() = p;
99 return *this;
102 template <typename U>
103 ThisType& operator=(U&& p) {
104 this->ref() = std::move(p);
105 return *this;
108 template <typename U>
109 T& operator+=(const U& rhs) {
110 return ref() += rhs;
112 template <typename U>
113 T& operator-=(const U& rhs) {
114 return ref() -= rhs;
116 template <typename U>
117 T& operator*=(const U& rhs) {
118 return ref() *= rhs;
120 template <typename U>
121 T& operator/=(const U& rhs) {
122 return ref() /= rhs;
124 template <typename U>
125 T& operator&=(const U& rhs) {
126 return ref() &= rhs;
128 template <typename U>
129 T& operator|=(const U& rhs) {
130 return ref() |= rhs;
132 T& operator++() { return ++ref(); }
133 T& operator--() { return --ref(); }
134 T operator++(int) { return ref()++; }
135 T operator--(int) { return ref()--; }
137 T& ref() {
138 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
139 if (!AutoNoteSingleThreadedRegion::count) {
140 check.check();
142 #endif
143 return value;
146 const T& ref() const {
147 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
148 if (!AutoNoteSingleThreadedRegion::count) {
149 check.check();
151 #endif
152 return value;
155 T& refNoCheck() { return value; }
156 const T& refNoCheck() const { return value; }
158 static size_t offsetOfValue() { return offsetof(ThisType, value); }
160 private:
161 T value;
162 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
163 Check check;
164 #endif
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>;
173 public:
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>;
186 public:
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
196 public:
197 inline void check() const {}
198 #endif
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
209 ThreadId id;
211 public:
212 CheckThreadLocal() : id(ThreadId::ThisThreadId()) {}
214 void check() const;
215 #endif
218 class CheckContextLocal {
219 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
220 JSContext* cx_;
222 public:
223 explicit CheckContextLocal(JSContext* cx) : cx_(cx) {}
225 void check() const;
226 #else
227 public:
228 explicit CheckContextLocal(JSContext* cx) {}
229 #endif
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
243 // access data.
244 enum class AllowedHelperThread {
245 None,
246 GCTask,
247 IonCompile,
248 GCTaskOrIonCompile,
251 template <AllowedHelperThread Helper>
252 class CheckMainThread {
253 public:
254 void check() const;
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
281 public:
282 void check() const;
283 #endif
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;
307 public:
308 template <typename... Args>
309 explicit ProtectedDataWriteOnce(Args&&... args)
310 : value(std::forward<Args>(args)...)
311 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
313 nwrites(0)
314 #endif
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) {
325 if (ref() != p) {
326 this->writeRef() = p;
328 return *this;
331 const T& ref() const { return value; }
333 T& writeRef() {
334 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
335 if (!AutoNoteSingleThreadedRegion::count) {
336 check.check();
338 // Despite the WriteOnce name, actually allow two writes to accommodate
339 // data that is cleared during teardown.
340 MOZ_ASSERT(++nwrites <= 2);
341 #endif
342 return value;
345 private:
346 T value;
347 #ifdef JS_HAS_PROTECTED_DATA_CHECKS
348 Check check;
349 size_t nwrites;
350 #endif
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
362 } // namespace js
364 #endif // threading_ProtectedData_h