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/PlatformMutex.h"
16 // - Mutex, a non-recursive mutex
17 // - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
18 // locked and unlocked
19 // - MutexAutoUnlock, complementary sibling to MutexAutoLock
21 // - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
22 // - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
23 // an OffTheBooksMutex.
25 // Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
26 // calls to Lock and Unlock.
31 * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
32 * include leak checking. Sometimes you want to intentionally "leak" a mutex
33 * until shutdown; in these cases, OffTheBooksMutex is for you.
35 class OffTheBooksMutex
: public detail::MutexImpl
, BlockingResourceBase
{
38 * @param aName A name which can reference this lock
39 * @returns If failure, nullptr
40 * If success, a valid Mutex* which must be destroyed
41 * by Mutex::DestroyMutex()
43 explicit OffTheBooksMutex(const char* aName
)
44 : BlockingResourceBase(aName
, eMutex
)
47 mOwningThread(nullptr)
54 MOZ_ASSERT(!mOwningThread
, "destroying a still-owned lock!");
62 void Lock() { this->lock(); }
65 * Try to lock this mutex, returning true if we were successful.
67 [[nodiscard
]] bool TryLock() { return this->tryLock(); }
72 void Unlock() { this->unlock(); }
75 * Assert that the current thread owns this mutex in debug builds.
77 * Does nothing in non-debug builds.
79 void AssertCurrentThreadOwns() const {}
82 * Assert that the current thread does not own this mutex.
84 * Note that this function is not implemented for debug builds *and*
85 * non-debug builds due to difficulties in dealing with memory ordering.
87 * It is therefore mostly useful as documentation.
89 void AssertNotCurrentThreadOwns() const {}
93 [[nodiscard
]] bool TryLock();
96 void AssertCurrentThreadOwns() const;
98 void AssertNotCurrentThreadOwns() const {
102 #endif // ifndef DEBUG
106 OffTheBooksMutex(const OffTheBooksMutex
&);
107 OffTheBooksMutex
& operator=(const OffTheBooksMutex
&);
109 friend class OffTheBooksCondVar
;
112 PRThread
* mOwningThread
;
118 * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
119 * mutex within a scope, instead of calling Lock/Unlock directly.
121 class Mutex
: public OffTheBooksMutex
{
123 explicit Mutex(const char* aName
) : OffTheBooksMutex(aName
) {
124 MOZ_COUNT_CTOR(Mutex
);
127 MOZ_COUNTED_DTOR(Mutex
)
132 Mutex
& operator=(const Mutex
&);
136 template <typename T
>
137 class MOZ_RAII BaseAutoUnlock
;
141 * Acquires the Mutex when it enters scope, and releases it when it leaves
144 * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
146 template <typename T
>
147 class MOZ_RAII BaseAutoLock
{
151 * The constructor aquires the given lock. The destructor
154 * @param aLock A valid mozilla::Mutex* returned by
155 * mozilla::Mutex::NewMutex.
157 explicit BaseAutoLock(T aLock
) : mLock(aLock
) { mLock
.Lock(); }
159 ~BaseAutoLock(void) { mLock
.Unlock(); }
161 // Assert that aLock is the mutex passed to the constructor and that the
162 // current thread owns the mutex. In coding patterns such as:
164 // void LockedMethod(const BaseAutoLock<T>& aProofOfLock)
166 // aProofOfLock.AssertOwns(mMutex);
170 // Without this assertion, it could be that mMutex is not actually
171 // locked. It's possible to have code like:
173 // BaseAutoLock lock(someMutex);
175 // BaseAutoUnlock unlock(someMutex);
177 // LockedMethod(lock);
179 // and in such a case, simply asserting that the mutex pointers match is not
180 // sufficient; mutex ownership must be asserted as well.
182 // Note that if you are going to use the coding pattern presented above, you
183 // should use this method in preference to using AssertCurrentThreadOwns on
184 // the mutex you expected to be held, since this method provides stronger
186 void AssertOwns(const T
& aMutex
) const {
187 MOZ_ASSERT(&aMutex
== &mLock
);
188 mLock
.AssertCurrentThreadOwns();
193 BaseAutoLock(BaseAutoLock
&);
194 BaseAutoLock
& operator=(BaseAutoLock
&);
195 static void* operator new(size_t) noexcept(true);
197 friend class BaseAutoUnlock
<T
>;
202 template <typename MutexType
>
203 BaseAutoLock(MutexType
&) -> BaseAutoLock
<MutexType
&>;
204 } // namespace detail
206 typedef detail::BaseAutoLock
<Mutex
&> MutexAutoLock
;
207 typedef detail::BaseAutoLock
<OffTheBooksMutex
&> OffTheBooksMutexAutoLock
;
212 * Releases the Mutex when it enters scope, and re-acquires it when it leaves
215 * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
217 template <typename T
>
218 class MOZ_RAII BaseAutoUnlock
{
220 explicit BaseAutoUnlock(T aLock
) : mLock(aLock
) { mLock
.Unlock(); }
222 explicit BaseAutoUnlock(BaseAutoLock
<T
>& aAutoLock
) : mLock(aAutoLock
.mLock
) {
223 NS_ASSERTION(mLock
, "null lock");
227 ~BaseAutoUnlock() { mLock
.Lock(); }
231 BaseAutoUnlock(BaseAutoUnlock
&);
232 BaseAutoUnlock
& operator=(BaseAutoUnlock
&);
233 static void* operator new(size_t) noexcept(true);
238 template <typename MutexType
>
239 BaseAutoUnlock(MutexType
&) -> BaseAutoUnlock
<MutexType
&>;
240 } // namespace detail
242 typedef detail::BaseAutoUnlock
<Mutex
&> MutexAutoUnlock
;
243 typedef detail::BaseAutoUnlock
<OffTheBooksMutex
&> OffTheBooksMutexAutoUnlock
;
245 } // namespace mozilla
247 #endif // ifndef mozilla_Mutex_h