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"
12 #include "nsISupports.h"
17 // - Mutex, a non-recursive mutex
18 // - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
19 // locked and unlocked
20 // - MutexAutoUnlock, complementary sibling to MutexAutoLock
22 // - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
23 // - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
24 // an OffTheBooksMutex.
26 // Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
27 // calls to Lock and Unlock.
32 * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
33 * include leak checking. Sometimes you want to intentionally "leak" a mutex
34 * until shutdown; in these cases, OffTheBooksMutex is for you.
36 class OffTheBooksMutex
: public detail::MutexImpl
, BlockingResourceBase
{
39 * @param aName A name which can reference this lock
40 * @returns If failure, nullptr
41 * If success, a valid Mutex* which must be destroyed
42 * by Mutex::DestroyMutex()
44 explicit OffTheBooksMutex(const char* aName
)
45 : BlockingResourceBase(aName
, eMutex
)
48 mOwningThread(nullptr)
55 MOZ_ASSERT(!mOwningThread
, "destroying a still-owned lock!");
63 void Lock() { this->lock(); }
66 * Try to lock this mutex, returning true if we were successful.
68 [[nodiscard
]] bool TryLock() { return this->tryLock(); }
73 void Unlock() { this->unlock(); }
76 * Assert that the current thread owns this mutex in debug builds.
78 * Does nothing in non-debug builds.
80 void AssertCurrentThreadOwns() const {}
83 * Assert that the current thread does not own this mutex.
85 * Note that this function is not implemented for debug builds *and*
86 * non-debug builds due to difficulties in dealing with memory ordering.
88 * It is therefore mostly useful as documentation.
90 void AssertNotCurrentThreadOwns() const {}
94 [[nodiscard
]] bool TryLock();
97 void AssertCurrentThreadOwns() const;
99 void AssertNotCurrentThreadOwns() const {
103 #endif // ifndef DEBUG
107 OffTheBooksMutex(const OffTheBooksMutex
&);
108 OffTheBooksMutex
& operator=(const OffTheBooksMutex
&);
110 friend class OffTheBooksCondVar
;
113 PRThread
* mOwningThread
;
119 * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
120 * mutex within a scope, instead of calling Lock/Unlock directly.
122 class Mutex
: public OffTheBooksMutex
{
124 explicit Mutex(const char* aName
) : OffTheBooksMutex(aName
) {
125 MOZ_COUNT_CTOR(Mutex
);
128 MOZ_COUNTED_DTOR(Mutex
)
133 Mutex
& operator=(const Mutex
&);
137 template <typename T
>
138 class MOZ_RAII BaseAutoUnlock
;
142 * Acquires the Mutex when it enters scope, and releases it when it leaves
145 * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
147 template <typename T
>
148 class MOZ_RAII BaseAutoLock
{
152 * The constructor aquires the given lock. The destructor
155 * @param aLock A valid mozilla::Mutex* returned by
156 * mozilla::Mutex::NewMutex.
158 explicit BaseAutoLock(T aLock
) : mLock(aLock
) { mLock
.Lock(); }
160 ~BaseAutoLock(void) { mLock
.Unlock(); }
162 // Assert that aLock is the mutex passed to the constructor and that the
163 // current thread owns the mutex. In coding patterns such as:
165 // void LockedMethod(const BaseAutoLock<T>& aProofOfLock)
167 // aProofOfLock.AssertOwns(mMutex);
171 // Without this assertion, it could be that mMutex is not actually
172 // locked. It's possible to have code like:
174 // BaseAutoLock lock(someMutex);
176 // BaseAutoUnlock unlock(someMutex);
178 // LockedMethod(lock);
180 // and in such a case, simply asserting that the mutex pointers match is not
181 // sufficient; mutex ownership must be asserted as well.
183 // Note that if you are going to use the coding pattern presented above, you
184 // should use this method in preference to using AssertCurrentThreadOwns on
185 // the mutex you expected to be held, since this method provides stronger
187 void AssertOwns(const T
& aMutex
) const {
188 MOZ_ASSERT(&aMutex
== &mLock
);
189 mLock
.AssertCurrentThreadOwns();
194 BaseAutoLock(BaseAutoLock
&);
195 BaseAutoLock
& operator=(BaseAutoLock
&);
196 static void* operator new(size_t) noexcept(true);
198 friend class BaseAutoUnlock
<T
>;
203 template <typename MutexType
>
204 BaseAutoLock(MutexType
&) -> BaseAutoLock
<MutexType
&>;
205 } // namespace detail
207 typedef detail::BaseAutoLock
<Mutex
&> MutexAutoLock
;
208 typedef detail::BaseAutoLock
<OffTheBooksMutex
&> OffTheBooksMutexAutoLock
;
213 * Releases the Mutex when it enters scope, and re-acquires it when it leaves
216 * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
218 template <typename T
>
219 class MOZ_RAII BaseAutoUnlock
{
221 explicit BaseAutoUnlock(T aLock
) : mLock(aLock
) { mLock
.Unlock(); }
223 explicit BaseAutoUnlock(BaseAutoLock
<T
>& aAutoLock
) : mLock(aAutoLock
.mLock
) {
224 NS_ASSERTION(mLock
, "null lock");
228 ~BaseAutoUnlock() { mLock
.Lock(); }
232 BaseAutoUnlock(BaseAutoUnlock
&);
233 BaseAutoUnlock
& operator=(BaseAutoUnlock
&);
234 static void* operator new(size_t) noexcept(true);
239 template <typename MutexType
>
240 BaseAutoUnlock(MutexType
&) -> BaseAutoUnlock
<MutexType
&>;
241 } // namespace detail
243 typedef detail::BaseAutoUnlock
<Mutex
&> MutexAutoUnlock
;
244 typedef detail::BaseAutoUnlock
<OffTheBooksMutex
&> OffTheBooksMutexAutoUnlock
;
246 } // namespace mozilla
248 #endif // ifndef mozilla_Mutex_h