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"
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
24 class MOZ_CAPABILITY("monitor") Monitor
{
26 explicit Monitor(const char* aName
)
27 : mMutex(aName
), mCondVar(mMutex
, "[Monitor.mCondVar]") {}
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();
54 Monitor(const Monitor
&) = delete;
55 Monitor
& operator=(const Monitor
&) = delete;
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
{
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
)
87 MOZ_COUNT_CTOR(MonitorSingleWriter
);
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) {
98 if (!mOwner
->OnWritingThread()) {
99 AssertCurrentThreadOwns();
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");
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
124 template <typename T
>
125 class MOZ_SCOPED_CAPABILITY MOZ_STACK_CLASS BaseMonitorAutoLock
{
127 explicit BaseMonitorAutoLock(T
& aMonitor
) MOZ_CAPABILITY_ACQUIRE(aMonitor
)
128 : mMonitor(&aMonitor
) {
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.
137 mMonitor
->AssertCurrentThreadOwns();
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)
153 // aProofOfLock.AssertOwns(mMonitor);
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);
162 // BaseAutoUnlock unlock(someMonitor);
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
173 void AssertOwns(const T
& aMonitor
) const MOZ_ASSERT_CAPABILITY(aMonitor
) {
174 MOZ_ASSERT(&aMonitor
== mMonitor
);
175 mMonitor
->AssertCurrentThreadOwns();
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
;
189 } // namespace detail
190 typedef detail::BaseMonitorAutoLock
<Monitor
> MonitorAutoLock
;
191 typedef detail::BaseMonitorAutoLock
<MonitorSingleWriter
>
192 MonitorSingleWriterAutoLock
;
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)
199 // MutexSingleWriterAutoLockOnThread(lock, mutex)
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.
214 template <typename T
>
215 class MOZ_STACK_CLASS MOZ_SCOPED_CAPABILITY BaseMonitorAutoUnlock
{
217 explicit BaseMonitorAutoUnlock(T
& aMonitor
)
218 MOZ_SCOPED_UNLOCK_RELEASE(aMonitor
)
219 : mMonitor(&aMonitor
) {
223 ~BaseMonitorAutoUnlock() MOZ_SCOPED_UNLOCK_REACQUIRE() { mMonitor
->Lock(); }
226 BaseMonitorAutoUnlock() = delete;
227 BaseMonitorAutoUnlock(const BaseMonitorAutoUnlock
&) = delete;
228 BaseMonitorAutoUnlock
& operator=(const BaseMonitorAutoUnlock
&) = delete;
229 static void* operator new(size_t) noexcept(true);
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
245 class MOZ_SCOPED_CAPABILITY MOZ_STACK_CLASS ReleasableMonitorAutoLock
{
247 explicit ReleasableMonitorAutoLock(Monitor
& aMonitor
)
248 MOZ_CAPABILITY_ACQUIRE(aMonitor
)
249 : mMonitor(&aMonitor
) {
254 ~ReleasableMonitorAutoLock() MOZ_CAPABILITY_RELEASE() {
260 // See BaseMonitorAutoLock::Wait
262 mMonitor
->AssertCurrentThreadOwns(); // someone could have called Unlock()
265 CVStatus
Wait(TimeDuration aDuration
) {
266 mMonitor
->AssertCurrentThreadOwns();
267 return mMonitor
->Wait(aDuration
);
276 mMonitor
->NotifyAll();
279 // Allow dropping the lock prematurely; for example to support something like:
281 // MonitorAutoLock lock(mMonitor);
285 // MethodThatCantBeCalledWithLock()
289 void Unlock() MOZ_CAPABILITY_RELEASE() {
294 void Lock() MOZ_CAPABILITY_ACQUIRE() {
295 MOZ_ASSERT(!mLocked
);
299 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY() {
300 mMonitor
->AssertCurrentThreadOwns();
307 ReleasableMonitorAutoLock() = delete;
308 ReleasableMonitorAutoLock(const ReleasableMonitorAutoLock
&) = delete;
309 ReleasableMonitorAutoLock
& operator=(const ReleasableMonitorAutoLock
&) =
311 static void* operator new(size_t) noexcept(true);
314 } // namespace mozilla
316 #endif // mozilla_Monitor_h