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/. */
6 
7 #ifndef mozilla_Mutex_h
8 #define mozilla_Mutex_h
9 
10 #include "prlock.h"
11 
12 #include "mozilla/BlockingResourceBase.h"
13 #include "mozilla/GuardObjects.h"
14 
15 //
16 // Provides:
17 //
18 //  - Mutex, a non-recursive mutex
19 //  - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
20 //    locked and unlocked
21 //  - MutexAutoUnlock, complementary sibling to MutexAutoLock
22 //
23 //  - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
24 //  - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
25 //    an OffTheBooksMutex.
26 //
27 // Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
28 // calls to Lock and Unlock.
29 //
30 namespace mozilla {
31 
32 /**
33  * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
34  * include leak checking.  Sometimes you want to intentionally "leak" a mutex
35  * until shutdown; in these cases, OffTheBooksMutex is for you.
36  */
37 class OffTheBooksMutex : BlockingResourceBase
38 {
39 public:
40   /**
41    * @param aName A name which can reference this lock
42    * @returns If failure, nullptr
43    *          If success, a valid Mutex* which must be destroyed
44    *          by Mutex::DestroyMutex()
45    **/
OffTheBooksMutex(const char * aName)46   explicit OffTheBooksMutex(const char* aName)
47     : BlockingResourceBase(aName, eMutex)
48   {
49     mLock = PR_NewLock();
50     if (!mLock) {
51       NS_RUNTIMEABORT("Can't allocate mozilla::Mutex");
52     }
53   }
54 
~OffTheBooksMutex()55   ~OffTheBooksMutex()
56   {
57     NS_ASSERTION(mLock,
58                  "improperly constructed Lock or double free");
59     // NSPR does consistency checks for us
60     PR_DestroyLock(mLock);
61     mLock = 0;
62   }
63 
64 #ifndef DEBUG
65   /**
66    * Lock
67    * @see prlock.h
68    **/
Lock()69   void Lock() { PR_Lock(mLock); }
70 
71   /**
72    * Unlock
73    * @see prlock.h
74    **/
Unlock()75   void Unlock() { PR_Unlock(mLock); }
76 
77   /**
78    * AssertCurrentThreadOwns
79    * @see prlock.h
80    **/
AssertCurrentThreadOwns()81   void AssertCurrentThreadOwns() const {}
82 
83   /**
84    * AssertNotCurrentThreadOwns
85    * @see prlock.h
86    **/
AssertNotCurrentThreadOwns()87   void AssertNotCurrentThreadOwns() const {}
88 
89 #else
90   void Lock();
91   void Unlock();
92 
AssertCurrentThreadOwns()93   void AssertCurrentThreadOwns() const
94   {
95     PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mLock);
96   }
97 
AssertNotCurrentThreadOwns()98   void AssertNotCurrentThreadOwns() const
99   {
100     // FIXME bug 476536
101   }
102 
103 #endif  // ifndef DEBUG
104 
105 private:
106   OffTheBooksMutex();
107   OffTheBooksMutex(const OffTheBooksMutex&);
108   OffTheBooksMutex& operator=(const OffTheBooksMutex&);
109 
110   PRLock* mLock;
111 
112   friend class CondVar;
113 
114   // MozPromise needs to access mLock for debugging purpose.
115   template<typename, typename, bool>
116   friend class MozPromise;
117 };
118 
119 /**
120  * Mutex
121  * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
122  * mutex within a scope, instead of calling Lock/Unlock directly.
123  */
124 class Mutex : public OffTheBooksMutex
125 {
126 public:
Mutex(const char * aName)127   explicit Mutex(const char* aName)
128     : OffTheBooksMutex(aName)
129   {
130     MOZ_COUNT_CTOR(Mutex);
131   }
132 
~Mutex()133   ~Mutex()
134   {
135     MOZ_COUNT_DTOR(Mutex);
136   }
137 
138 private:
139   Mutex();
140   Mutex(const Mutex&);
141   Mutex& operator=(const Mutex&);
142 };
143 
144 /**
145  * MutexAutoLock
146  * Acquires the Mutex when it enters scope, and releases it when it leaves
147  * scope.
148  *
149  * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
150  */
151 template<typename T>
152 class MOZ_RAII BaseAutoLock
153 {
154 public:
155   /**
156    * Constructor
157    * The constructor aquires the given lock.  The destructor
158    * releases the lock.
159    *
160    * @param aLock A valid mozilla::Mutex* returned by
161    *              mozilla::Mutex::NewMutex.
162    **/
BaseAutoLock(T & aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)163   explicit BaseAutoLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
164     : mLock(&aLock)
165   {
166     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
167     NS_ASSERTION(mLock, "null mutex");
168     mLock->Lock();
169   }
170 
~BaseAutoLock(void)171   ~BaseAutoLock(void)
172   {
173     mLock->Unlock();
174   }
175 
176 private:
177   BaseAutoLock();
178   BaseAutoLock(BaseAutoLock&);
179   BaseAutoLock& operator=(BaseAutoLock&);
180   static void* operator new(size_t) CPP_THROW_NEW;
181 
182   T* mLock;
183   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
184 };
185 
186 typedef BaseAutoLock<Mutex> MutexAutoLock;
187 typedef BaseAutoLock<OffTheBooksMutex> OffTheBooksMutexAutoLock;
188 
189 /**
190  * MutexAutoUnlock
191  * Releases the Mutex when it enters scope, and re-acquires it when it leaves
192  * scope.
193  *
194  * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
195  */
196 template<typename T>
197 class MOZ_RAII BaseAutoUnlock
198 {
199 public:
BaseAutoUnlock(T & aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)200   explicit BaseAutoUnlock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
201     : mLock(&aLock)
202   {
203     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
204     NS_ASSERTION(mLock, "null lock");
205     mLock->Unlock();
206   }
207 
~BaseAutoUnlock()208   ~BaseAutoUnlock()
209   {
210     mLock->Lock();
211   }
212 
213 private:
214   BaseAutoUnlock();
215   BaseAutoUnlock(BaseAutoUnlock&);
216   BaseAutoUnlock& operator=(BaseAutoUnlock&);
217   static void* operator new(size_t) CPP_THROW_NEW;
218 
219   T* mLock;
220   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
221 };
222 
223 typedef BaseAutoUnlock<Mutex> MutexAutoUnlock;
224 typedef BaseAutoUnlock<OffTheBooksMutex> OffTheBooksMutexAutoUnlock;
225 
226 } // namespace mozilla
227 
228 
229 #endif // ifndef mozilla_Mutex_h
230