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_Mutex_h
8 #define mozilla_Mutex_h
10 #include "mozilla/BlockingResourceBase.h"
11 #include "mozilla/ThreadSafety.h"
12 #include "mozilla/PlatformMutex.h"
13 #include "mozilla/Maybe.h"
14 #include "nsISupports.h"
19 // - Mutex, a non-recursive mutex
20 // - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
21 // locked and unlocked
22 // - MutexAutoUnlock, complementary sibling to MutexAutoLock
24 // - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
25 // - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
26 // an OffTheBooksMutex.
28 // Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
29 // calls to Lock and Unlock.
34 * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
35 * include leak checking. Sometimes you want to intentionally "leak" a mutex
36 * until shutdown; in these cases, OffTheBooksMutex is for you.
38 class MOZ_CAPABILITY("mutex") OffTheBooksMutex
: public detail::MutexImpl
,
39 BlockingResourceBase
{
42 * @param aName A name which can reference this lock
43 * @returns If failure, nullptr
44 * If success, a valid Mutex* which must be destroyed
45 * by Mutex::DestroyMutex()
47 explicit OffTheBooksMutex(const char* aName
)
48 : BlockingResourceBase(aName
, eMutex
)
51 mOwningThread(nullptr)
58 MOZ_ASSERT(!mOwningThread
, "destroying a still-owned lock!");
66 void Lock() MOZ_CAPABILITY_ACQUIRE() { this->lock(); }
69 * Try to lock this mutex, returning true if we were successful.
71 [[nodiscard
]] bool TryLock() MOZ_TRY_ACQUIRE(true) { return this->tryLock(); }
76 void Unlock() MOZ_CAPABILITY_RELEASE() { this->unlock(); }
79 * Assert that the current thread owns this mutex in debug builds.
81 * Does nothing in non-debug builds.
83 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this) {}
86 * Assert that the current thread does not own this mutex.
88 * Note that this function is not implemented for debug builds *and*
89 * non-debug builds due to difficulties in dealing with memory ordering.
91 * It is therefore mostly useful as documentation.
93 void AssertNotCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(!this) {}
96 void Lock() MOZ_CAPABILITY_ACQUIRE();
98 [[nodiscard
]] bool TryLock() MOZ_TRY_ACQUIRE(true);
99 void Unlock() MOZ_CAPABILITY_RELEASE();
101 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this);
102 void AssertNotCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(!this) {
105 #endif // ifndef DEBUG
108 OffTheBooksMutex() = delete;
109 OffTheBooksMutex(const OffTheBooksMutex
&) = delete;
110 OffTheBooksMutex
& operator=(const OffTheBooksMutex
&) = delete;
112 friend class OffTheBooksCondVar
;
115 PRThread
* mOwningThread
;
121 * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
122 * mutex within a scope, instead of calling Lock/Unlock directly.
124 class Mutex
: public OffTheBooksMutex
{
126 explicit Mutex(const char* aName
) : OffTheBooksMutex(aName
) {
127 MOZ_COUNT_CTOR(Mutex
);
130 MOZ_COUNTED_DTOR(Mutex
)
134 Mutex(const Mutex
&) = delete;
135 Mutex
& operator=(const Mutex
&) = delete;
141 * Mutex where a single writer exists, so that reads from the same thread
142 * will not generate data races or consistency issues.
144 * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
145 * mutex within a scope, instead of calling Lock/Unlock directly.
147 * This requires an object implementing Mutex's SingleWriterLockOwner, so
148 * we can do correct-thread checks.
150 // Subclass this in the object owning the mutex
151 class SingleWriterLockOwner
{
153 SingleWriterLockOwner() = default;
154 ~SingleWriterLockOwner() = default;
156 virtual bool OnWritingThread() const = 0;
159 class MutexSingleWriter
: public OffTheBooksMutex
{
161 // aOwner should be the object that contains the mutex, typically. We
162 // will use that object (which must have a lifetime the same or greater
163 // than this object) to verify that we're running on the correct thread,
164 // typically only in DEBUG builds
165 explicit MutexSingleWriter(const char* aName
, SingleWriterLockOwner
* aOwner
)
166 : OffTheBooksMutex(aName
)
172 MOZ_COUNT_CTOR(MutexSingleWriter
);
176 MOZ_COUNTED_DTOR(MutexSingleWriter
)
179 * Statically assert that we're on the only thread that modifies data
180 * guarded by this Mutex. This allows static checking for the pattern of
181 * having a single thread modify a set of data, and read it (under lock)
182 * on other threads, and reads on the thread that modifies it doesn't
183 * require a lock. This doesn't solve the issue of some data under the
184 * Mutex following this pattern, and other data under the mutex being
185 * written from multiple threads.
187 * We could set the writing thread and dynamically check it in debug
188 * builds, but this doesn't. We could also use thread-safety/capability
189 * system to provide direct thread assertions.
191 void AssertOnWritingThread() const MOZ_ASSERT_CAPABILITY(this) {
192 MOZ_ASSERT(mOwner
->OnWritingThread());
194 void AssertOnWritingThreadOrHeld() const MOZ_ASSERT_CAPABILITY(this) {
196 if (!mOwner
->OnWritingThread()) {
197 AssertCurrentThreadOwns();
204 SingleWriterLockOwner
* mOwner
MOZ_UNSAFE_REF(
205 "This is normally the object that contains the MonitorSingleWriter, so "
206 "we don't want to hold a reference to ourselves");
209 MutexSingleWriter() = delete;
210 MutexSingleWriter(const MutexSingleWriter
&) = delete;
211 MutexSingleWriter
& operator=(const MutexSingleWriter
&) = delete;
215 template <typename T
>
216 class MOZ_RAII BaseAutoUnlock
;
220 * Acquires the Mutex when it enters scope, and releases it when it leaves
223 * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
225 template <typename T
>
226 class MOZ_RAII MOZ_SCOPED_CAPABILITY BaseAutoLock
{
230 * The constructor acquires the given lock. The destructor
233 * @param aLock A valid mozilla::Mutex* returned by
234 * mozilla::Mutex::NewMutex.
236 explicit BaseAutoLock(T aLock
) MOZ_CAPABILITY_ACQUIRE(aLock
) : mLock(aLock
) {
240 ~BaseAutoLock(void) MOZ_CAPABILITY_RELEASE() { mLock
.Unlock(); }
242 // Assert that aLock is the mutex passed to the constructor and that the
243 // current thread owns the mutex. In coding patterns such as:
245 // void LockedMethod(const BaseAutoLock<T>& aProofOfLock)
247 // aProofOfLock.AssertOwns(mMutex);
251 // Without this assertion, it could be that mMutex is not actually
252 // locked. It's possible to have code like:
254 // BaseAutoLock lock(someMutex);
256 // BaseAutoUnlock unlock(someMutex);
258 // LockedMethod(lock);
260 // and in such a case, simply asserting that the mutex pointers match is not
261 // sufficient; mutex ownership must be asserted as well.
263 // Note that if you are going to use the coding pattern presented above, you
264 // should use this method in preference to using AssertCurrentThreadOwns on
265 // the mutex you expected to be held, since this method provides stronger
267 void AssertOwns(const T
& aMutex
) const MOZ_ASSERT_CAPABILITY(aMutex
) {
268 MOZ_ASSERT(&aMutex
== &mLock
);
269 mLock
.AssertCurrentThreadOwns();
273 BaseAutoLock() = delete;
274 BaseAutoLock(BaseAutoLock
&) = delete;
275 BaseAutoLock
& operator=(BaseAutoLock
&) = delete;
276 static void* operator new(size_t) noexcept(true);
278 friend class BaseAutoUnlock
<T
>;
283 template <typename MutexType
>
284 BaseAutoLock(MutexType
&) -> BaseAutoLock
<MutexType
&>;
285 } // namespace detail
287 typedef detail::BaseAutoLock
<Mutex
&> MutexAutoLock
;
288 typedef detail::BaseAutoLock
<MutexSingleWriter
&> MutexSingleWriterAutoLock
;
289 typedef detail::BaseAutoLock
<OffTheBooksMutex
&> OffTheBooksMutexAutoLock
;
291 // Specialization of Maybe<*AutoLock> for space efficiency and to silence
292 // thread-safety analysis, which cannot track what's going on.
293 template <class MutexType
>
294 class Maybe
<detail::BaseAutoLock
<MutexType
&>> {
296 Maybe() : mLock(nullptr) {}
297 ~Maybe() MOZ_NO_THREAD_SAFETY_ANALYSIS
{
303 constexpr bool isSome() const { return mLock
; }
304 constexpr bool isNothing() const { return !mLock
; }
306 void emplace(MutexType
& aMutex
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
307 MOZ_RELEASE_ASSERT(!mLock
);
312 void reset() MOZ_NO_THREAD_SAFETY_ANALYSIS
{
323 // Use if we've done AssertOnWritingThread(), and then later need to take the
324 // lock to write to a protected member. Instead of
325 // MutexSingleWriterAutoLock lock(mutex)
327 // MutexSingleWriterAutoLockOnThread(lock, mutex)
328 #define MutexSingleWriterAutoLockOnThread(lock, mutex) \
329 MOZ_PUSH_IGNORE_THREAD_SAFETY \
330 MutexSingleWriterAutoLock lock(mutex); \
331 MOZ_POP_THREAD_SAFETY
335 * ReleasableMutexAutoLock
336 * Acquires the Mutex when it enters scope, and releases it when it leaves
337 * scope. Allows calling Unlock (and Lock) as an alternative to
338 * MutexAutoUnlock; this can avoid an extra lock/unlock pair.
341 template <typename T
>
342 class MOZ_RAII MOZ_SCOPED_CAPABILITY ReleasableBaseAutoLock
{
346 * The constructor acquires the given lock. The destructor
349 * @param aLock A valid mozilla::Mutex& returned by
350 * mozilla::Mutex::NewMutex.
352 explicit ReleasableBaseAutoLock(T aLock
) MOZ_CAPABILITY_ACQUIRE(aLock
)
358 ~ReleasableBaseAutoLock(void) MOZ_CAPABILITY_RELEASE() {
364 void AssertOwns(const T
& aMutex
) const MOZ_ASSERT_CAPABILITY(aMutex
) {
365 MOZ_ASSERT(&aMutex
== &mLock
);
366 mLock
.AssertCurrentThreadOwns();
369 // Allow dropping the lock prematurely; for example to support something like:
371 // MutexAutoLock lock(mMutex);
375 // MethodThatCantBeCalledWithLock()
379 void Unlock() MOZ_CAPABILITY_RELEASE() {
384 void Lock() MOZ_CAPABILITY_ACQUIRE() {
385 MOZ_ASSERT(!mLocked
);
391 ReleasableBaseAutoLock() = delete;
392 ReleasableBaseAutoLock(ReleasableBaseAutoLock
&) = delete;
393 ReleasableBaseAutoLock
& operator=(ReleasableBaseAutoLock
&) = delete;
394 static void* operator new(size_t) noexcept(true);
400 template <typename MutexType
>
401 ReleasableBaseAutoLock(MutexType
&) -> ReleasableBaseAutoLock
<MutexType
&>;
402 } // namespace detail
404 typedef detail::ReleasableBaseAutoLock
<Mutex
&> ReleasableMutexAutoLock
;
409 * Releases the Mutex when it enters scope, and re-acquires it when it leaves
412 * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
414 template <typename T
>
415 class MOZ_RAII MOZ_SCOPED_CAPABILITY BaseAutoUnlock
{
417 explicit BaseAutoUnlock(T aLock
) MOZ_SCOPED_UNLOCK_RELEASE(aLock
)
422 explicit BaseAutoUnlock(BaseAutoLock
<T
>& aAutoLock
)
423 /* MOZ_CAPABILITY_RELEASE(aAutoLock.mLock) */
424 : mLock(aAutoLock
.mLock
) {
425 NS_ASSERTION(mLock
, "null lock");
429 ~BaseAutoUnlock() MOZ_SCOPED_UNLOCK_REACQUIRE() { mLock
.Lock(); }
432 BaseAutoUnlock() = delete;
433 BaseAutoUnlock(BaseAutoUnlock
&) = delete;
434 BaseAutoUnlock
& operator=(BaseAutoUnlock
&) = delete;
435 static void* operator new(size_t) noexcept(true);
440 template <typename MutexType
>
441 BaseAutoUnlock(MutexType
&) -> BaseAutoUnlock
<MutexType
&>;
442 } // namespace detail
444 typedef detail::BaseAutoUnlock
<Mutex
&> MutexAutoUnlock
;
445 typedef detail::BaseAutoUnlock
<MutexSingleWriter
&> MutexSingleWriterAutoUnlock
;
446 typedef detail::BaseAutoUnlock
<OffTheBooksMutex
&> OffTheBooksMutexAutoUnlock
;
451 * Tries to acquire the Mutex when it enters scope, and releases it when it
454 * MUCH PREFERRED to bare calls to Mutex.TryLock and Unlock.
456 template <typename T
>
457 class MOZ_RAII MOZ_SCOPED_CAPABILITY BaseAutoTryLock
{
459 explicit BaseAutoTryLock(T
& aLock
) MOZ_CAPABILITY_ACQUIRE(aLock
)
460 : mLock(aLock
.TryLock() ? &aLock
: nullptr) {}
462 ~BaseAutoTryLock() MOZ_CAPABILITY_RELEASE() {
469 explicit operator bool() const { return mLock
; }
472 BaseAutoTryLock(BaseAutoTryLock
&) = delete;
473 BaseAutoTryLock
& operator=(BaseAutoTryLock
&) = delete;
474 static void* operator new(size_t) noexcept(true);
478 } // namespace detail
480 typedef detail::BaseAutoTryLock
<Mutex
> MutexAutoTryLock
;
481 typedef detail::BaseAutoTryLock
<OffTheBooksMutex
> OffTheBooksMutexAutoTryLock
;
483 } // namespace mozilla
485 #endif // ifndef mozilla_Mutex_h