Bug 1838739 - Initialize result of SetAsGPUOutOfMemoryError. r=webgpu-reviewers,nical
[gecko.git] / xpcom / threads / Monitor.h
blob7bb91f9ad769aa0824fb4411e6fb961bd9fc69bf
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 mozilla_Monitor_h
8 #define mozilla_Monitor_h
10 #include "mozilla/CondVar.h"
11 #include "mozilla/Mutex.h"
13 namespace mozilla {
15 /**
16 * Monitor provides a *non*-reentrant monitor: *not* a Java-style
17 * monitor. If your code needs support for reentrancy, use
18 * ReentrantMonitor instead. (Rarely should reentrancy be needed.)
20 * Instead of directly calling Monitor methods, it's safer and simpler
21 * to instead use the RAII wrappers MonitorAutoLock and
22 * MonitorAutoUnlock.
24 class MOZ_CAPABILITY("monitor") Monitor {
25 public:
26 explicit Monitor(const char* aName)
27 : mMutex(aName), mCondVar(mMutex, "[Monitor.mCondVar]") {}
29 ~Monitor() = default;
31 void Lock() MOZ_CAPABILITY_ACQUIRE() { mMutex.Lock(); }
32 [[nodiscard]] bool TryLock() MOZ_TRY_ACQUIRE(true) {
33 return mMutex.TryLock();
35 void Unlock() MOZ_CAPABILITY_RELEASE() { mMutex.Unlock(); }
37 void Wait() MOZ_REQUIRES(this) { mCondVar.Wait(); }
38 CVStatus Wait(TimeDuration aDuration) MOZ_REQUIRES(this) {
39 return mCondVar.Wait(aDuration);
42 void Notify() { mCondVar.Notify(); }
43 void NotifyAll() { mCondVar.NotifyAll(); }
45 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this) {
46 mMutex.AssertCurrentThreadOwns();
48 void AssertNotCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(!this) {
49 mMutex.AssertNotCurrentThreadOwns();
52 private:
53 Monitor() = delete;
54 Monitor(const Monitor&) = delete;
55 Monitor& operator=(const Monitor&) = delete;
57 Mutex mMutex;
58 CondVar mCondVar;
61 /**
62 * MonitorSingleWriter
64 * Monitor where a single writer exists, so that reads from the same thread
65 * will not generate data races or consistency issues.
67 * When possible, use MonitorAutoLock/MonitorAutoUnlock to lock/unlock this
68 * monitor within a scope, instead of calling Lock/Unlock directly.
70 * This requires an object implementing Mutex's SingleWriterLockOwner, so
71 * we can do correct-thread checks.
74 class MonitorSingleWriter : public Monitor {
75 public:
76 // aOwner should be the object that contains the mutex, typically. We
77 // will use that object (which must have a lifetime the same or greater
78 // than this object) to verify that we're running on the correct thread,
79 // typically only in DEBUG builds
80 explicit MonitorSingleWriter(const char* aName, SingleWriterLockOwner* aOwner)
81 : Monitor(aName)
82 #ifdef DEBUG
84 mOwner(aOwner)
85 #endif
87 MOZ_COUNT_CTOR(MonitorSingleWriter);
88 MOZ_ASSERT(mOwner);
91 MOZ_COUNTED_DTOR(MonitorSingleWriter)
93 void AssertOnWritingThread() const MOZ_ASSERT_CAPABILITY(this) {
94 MOZ_ASSERT(mOwner->OnWritingThread());
96 void AssertOnWritingThreadOrHeld() const MOZ_ASSERT_CAPABILITY(this) {
97 #ifdef DEBUG
98 if (!mOwner->OnWritingThread()) {
99 AssertCurrentThreadOwns();
101 #endif
104 private:
105 #ifdef DEBUG
106 SingleWriterLockOwner* mOwner MOZ_UNSAFE_REF(
107 "This is normally the object that contains the MonitorSingleWriter, so "
108 "we don't want to hold a reference to ourselves");
109 #endif
111 MonitorSingleWriter() = delete;
112 MonitorSingleWriter(const MonitorSingleWriter&) = delete;
113 MonitorSingleWriter& operator=(const MonitorSingleWriter&) = delete;
117 * Lock the monitor for the lexical scope instances of this class are
118 * bound to (except for MonitorAutoUnlock in nested scopes).
120 * The monitor must be unlocked when instances of this class are
121 * created.
123 namespace detail {
124 template <typename T>
125 class MOZ_SCOPED_CAPABILITY MOZ_STACK_CLASS BaseMonitorAutoLock {
126 public:
127 explicit BaseMonitorAutoLock(T& aMonitor) MOZ_CAPABILITY_ACQUIRE(aMonitor)
128 : mMonitor(&aMonitor) {
129 mMonitor->Lock();
132 ~BaseMonitorAutoLock() MOZ_CAPABILITY_RELEASE() { mMonitor->Unlock(); }
133 // It's very hard to mess up MonitorAutoLock lock(mMonitor); ... lock.Wait().
134 // The only way you can fail to hold the lock when you call lock.Wait() is to
135 // use MonitorAutoUnlock. For now we'll ignore that case.
136 void Wait() {
137 mMonitor->AssertCurrentThreadOwns();
138 mMonitor->Wait();
140 CVStatus Wait(TimeDuration aDuration) {
141 mMonitor->AssertCurrentThreadOwns();
142 return mMonitor->Wait(aDuration);
145 void Notify() { mMonitor->Notify(); }
146 void NotifyAll() { mMonitor->NotifyAll(); }
148 // Assert that aLock is the monitor passed to the constructor and that the
149 // current thread owns the monitor. In coding patterns such as:
151 // void LockedMethod(const BaseAutoLock<T>& aProofOfLock)
152 // {
153 // aProofOfLock.AssertOwns(mMonitor);
154 // ...
155 // }
157 // Without this assertion, it could be that mMonitor is not actually
158 // locked. It's possible to have code like:
160 // BaseAutoLock lock(someMonitor);
161 // ...
162 // BaseAutoUnlock unlock(someMonitor);
163 // ...
164 // LockedMethod(lock);
166 // and in such a case, simply asserting that the monitor pointers match is not
167 // sufficient; monitor ownership must be asserted as well.
169 // Note that if you are going to use the coding pattern presented above, you
170 // should use this method in preference to using AssertCurrentThreadOwns on
171 // the mutex you expected to be held, since this method provides stronger
172 // guarantees.
173 void AssertOwns(const T& aMonitor) const MOZ_ASSERT_CAPABILITY(aMonitor) {
174 MOZ_ASSERT(&aMonitor == mMonitor);
175 mMonitor->AssertCurrentThreadOwns();
178 private:
179 BaseMonitorAutoLock() = delete;
180 BaseMonitorAutoLock(const BaseMonitorAutoLock&) = delete;
181 BaseMonitorAutoLock& operator=(const BaseMonitorAutoLock&) = delete;
182 static void* operator new(size_t) noexcept(true);
184 friend class MonitorAutoUnlock;
186 protected:
187 T* mMonitor;
189 } // namespace detail
190 typedef detail::BaseMonitorAutoLock<Monitor> MonitorAutoLock;
191 typedef detail::BaseMonitorAutoLock<MonitorSingleWriter>
192 MonitorSingleWriterAutoLock;
194 // clang-format off
195 // Use if we've done AssertOnWritingThread(), and then later need to take the
196 // lock to write to a protected member. Instead of
197 // MutexSingleWriterAutoLock lock(mutex)
198 // use
199 // MutexSingleWriterAutoLockOnThread(lock, mutex)
200 // clang-format on
201 #define MonitorSingleWriterAutoLockOnThread(lock, monitor) \
202 MOZ_PUSH_IGNORE_THREAD_SAFETY \
203 MonitorSingleWriterAutoLock lock(monitor); \
204 MOZ_POP_THREAD_SAFETY
207 * Unlock the monitor for the lexical scope instances of this class
208 * are bound to (except for MonitorAutoLock in nested scopes).
210 * The monitor must be locked by the current thread when instances of
211 * this class are created.
213 namespace detail {
214 template <typename T>
215 class MOZ_STACK_CLASS MOZ_SCOPED_CAPABILITY BaseMonitorAutoUnlock {
216 public:
217 explicit BaseMonitorAutoUnlock(T& aMonitor)
218 MOZ_SCOPED_UNLOCK_RELEASE(aMonitor)
219 : mMonitor(&aMonitor) {
220 mMonitor->Unlock();
223 ~BaseMonitorAutoUnlock() MOZ_SCOPED_UNLOCK_REACQUIRE() { mMonitor->Lock(); }
225 private:
226 BaseMonitorAutoUnlock() = delete;
227 BaseMonitorAutoUnlock(const BaseMonitorAutoUnlock&) = delete;
228 BaseMonitorAutoUnlock& operator=(const BaseMonitorAutoUnlock&) = delete;
229 static void* operator new(size_t) noexcept(true);
231 T* mMonitor;
233 } // namespace detail
234 typedef detail::BaseMonitorAutoUnlock<Monitor> MonitorAutoUnlock;
235 typedef detail::BaseMonitorAutoUnlock<MonitorSingleWriter>
236 MonitorSingleWriterAutoUnlock;
239 * Lock the monitor for the lexical scope instances of this class are
240 * bound to (except for MonitorAutoUnlock in nested scopes).
242 * The monitor must be unlocked when instances of this class are
243 * created.
245 class MOZ_SCOPED_CAPABILITY MOZ_STACK_CLASS ReleasableMonitorAutoLock {
246 public:
247 explicit ReleasableMonitorAutoLock(Monitor& aMonitor)
248 MOZ_CAPABILITY_ACQUIRE(aMonitor)
249 : mMonitor(&aMonitor) {
250 mMonitor->Lock();
251 mLocked = true;
254 ~ReleasableMonitorAutoLock() MOZ_CAPABILITY_RELEASE() {
255 if (mLocked) {
256 mMonitor->Unlock();
260 // See BaseMonitorAutoLock::Wait
261 void Wait() {
262 mMonitor->AssertCurrentThreadOwns(); // someone could have called Unlock()
263 mMonitor->Wait();
265 CVStatus Wait(TimeDuration aDuration) {
266 mMonitor->AssertCurrentThreadOwns();
267 return mMonitor->Wait(aDuration);
270 void Notify() {
271 MOZ_ASSERT(mLocked);
272 mMonitor->Notify();
274 void NotifyAll() {
275 MOZ_ASSERT(mLocked);
276 mMonitor->NotifyAll();
279 // Allow dropping the lock prematurely; for example to support something like:
280 // clang-format off
281 // MonitorAutoLock lock(mMonitor);
282 // ...
283 // if (foo) {
284 // lock.Unlock();
285 // MethodThatCantBeCalledWithLock()
286 // return;
287 // }
288 // clang-format on
289 void Unlock() MOZ_CAPABILITY_RELEASE() {
290 MOZ_ASSERT(mLocked);
291 mMonitor->Unlock();
292 mLocked = false;
294 void Lock() MOZ_CAPABILITY_ACQUIRE() {
295 MOZ_ASSERT(!mLocked);
296 mMonitor->Lock();
297 mLocked = true;
299 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY() {
300 mMonitor->AssertCurrentThreadOwns();
303 private:
304 bool mLocked;
305 Monitor* mMonitor;
307 ReleasableMonitorAutoLock() = delete;
308 ReleasableMonitorAutoLock(const ReleasableMonitorAutoLock&) = delete;
309 ReleasableMonitorAutoLock& operator=(const ReleasableMonitorAutoLock&) =
310 delete;
311 static void* operator new(size_t) noexcept(true);
314 } // namespace mozilla
316 #endif // mozilla_Monitor_h