• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_BASE_PLATFORM_MUTEX_H_
6 #define V8_BASE_PLATFORM_MUTEX_H_
7 
8 #include "src/base/base-export.h"
9 #include "src/base/lazy-instance.h"
10 #if V8_OS_WIN
11 #include "src/base/win32-headers.h"
12 #endif
13 #include "src/base/logging.h"
14 
15 #if V8_OS_POSIX
16 #include <pthread.h>  // NOLINT
17 #endif
18 
19 #if V8_OS_STARBOARD
20 #include "starboard/common/mutex.h"
21 #include "starboard/common/recursive_mutex.h"
22 #include "starboard/common/rwlock.h"
23 #endif
24 
25 namespace v8 {
26 namespace base {
27 
28 // ----------------------------------------------------------------------------
29 // Mutex - a replacement for std::mutex
30 //
31 // This class is a synchronization primitive that can be used to protect shared
32 // data from being simultaneously accessed by multiple threads. A mutex offers
33 // exclusive, non-recursive ownership semantics:
34 // - A calling thread owns a mutex from the time that it successfully calls
35 //   either |Lock()| or |TryLock()| until it calls |Unlock()|.
36 // - When a thread owns a mutex, all other threads will block (for calls to
37 //   |Lock()|) or receive a |false| return value (for |TryLock()|) if they
38 //   attempt to claim ownership of the mutex.
39 // A calling thread must not own the mutex prior to calling |Lock()| or
40 // |TryLock()|. The behavior of a program is undefined if a mutex is destroyed
41 // while still owned by some thread. The Mutex class is non-copyable.
42 
43 class V8_BASE_EXPORT Mutex final {
44  public:
45   Mutex();
46   Mutex(const Mutex&) = delete;
47   Mutex& operator=(const Mutex&) = delete;
48   ~Mutex();
49 
50   // Locks the given mutex. If the mutex is currently unlocked, it becomes
51   // locked and owned by the calling thread, and immediately. If the mutex
52   // is already locked by another thread, suspends the calling thread until
53   // the mutex is unlocked.
54   void Lock();
55 
56   // Unlocks the given mutex. The mutex is assumed to be locked and owned by
57   // the calling thread on entrance.
58   void Unlock();
59 
60   // Tries to lock the given mutex. Returns whether the mutex was
61   // successfully locked.
62   bool TryLock() V8_WARN_UNUSED_RESULT;
63 
64   // The implementation-defined native handle type.
65 #if V8_OS_POSIX
66   using NativeHandle = pthread_mutex_t;
67 #elif V8_OS_WIN
68   using NativeHandle = SRWLOCK;
69 #elif V8_OS_STARBOARD
70   using NativeHandle = SbMutex;
71 #endif
72 
native_handle()73   NativeHandle& native_handle() {
74     return native_handle_;
75   }
native_handle()76   const NativeHandle& native_handle() const {
77     return native_handle_;
78   }
79 
AssertHeld()80   V8_INLINE void AssertHeld() const { DCHECK_EQ(1, level_); }
AssertUnheld()81   V8_INLINE void AssertUnheld() const { DCHECK_EQ(0, level_); }
82 
83  private:
84   NativeHandle native_handle_;
85 #ifdef DEBUG
86   int level_;
87 #endif
88 
AssertHeldAndUnmark()89   V8_INLINE void AssertHeldAndUnmark() {
90 #ifdef DEBUG
91     DCHECK_EQ(1, level_);
92     level_--;
93 #endif
94   }
95 
AssertUnheldAndMark()96   V8_INLINE void AssertUnheldAndMark() {
97 #ifdef DEBUG
98     DCHECK_EQ(0, level_);
99     level_++;
100 #endif
101   }
102 
103   friend class ConditionVariable;
104 };
105 
106 // POD Mutex initialized lazily (i.e. the first time Pointer() is called).
107 // Usage:
108 //   static LazyMutex my_mutex = LAZY_MUTEX_INITIALIZER;
109 //
110 //   void my_function() {
111 //     MutexGuard guard(my_mutex.Pointer());
112 //     // Do something.
113 //   }
114 //
115 using LazyMutex = LazyStaticInstance<Mutex, DefaultConstructTrait<Mutex>,
116                                      ThreadSafeInitOnceTrait>::type;
117 
118 #define LAZY_MUTEX_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
119 
120 // -----------------------------------------------------------------------------
121 // RecursiveMutex - a replacement for std::recursive_mutex
122 //
123 // This class is a synchronization primitive that can be used to protect shared
124 // data from being simultaneously accessed by multiple threads. A recursive
125 // mutex offers exclusive, recursive ownership semantics:
126 // - A calling thread owns a recursive mutex for a period of time that starts
127 //   when it successfully calls either |Lock()| or |TryLock()|. During this
128 //   period, the thread may make additional calls to |Lock()| or |TryLock()|.
129 //   The period of ownership ends when the thread makes a matching number of
130 //   calls to |Unlock()|.
131 // - When a thread owns a recursive mutex, all other threads will block (for
132 //   calls to |Lock()|) or receive a |false| return value (for |TryLock()|) if
133 //   they attempt to claim ownership of the recursive mutex.
134 // - The maximum number of times that a recursive mutex may be locked is
135 //   unspecified, but after that number is reached, calls to |Lock()| will
136 //   probably abort the process and calls to |TryLock()| return false.
137 // The behavior of a program is undefined if a recursive mutex is destroyed
138 // while still owned by some thread. The RecursiveMutex class is non-copyable.
139 
140 class V8_BASE_EXPORT RecursiveMutex final {
141  public:
142   RecursiveMutex();
143   RecursiveMutex(const RecursiveMutex&) = delete;
144   RecursiveMutex& operator=(const RecursiveMutex&) = delete;
145   ~RecursiveMutex();
146 
147   // Locks the mutex. If another thread has already locked the mutex, a call to
148   // |Lock()| will block execution until the lock is acquired. A thread may call
149   // |Lock()| on a recursive mutex repeatedly. Ownership will only be released
150   // after the thread makes a matching number of calls to |Unlock()|.
151   // The behavior is undefined if the mutex is not unlocked before being
152   // destroyed, i.e. some thread still owns it.
153   void Lock();
154 
155   // Unlocks the mutex if its level of ownership is 1 (there was exactly one
156   // more call to |Lock()| than there were calls to unlock() made by this
157   // thread), reduces the level of ownership by 1 otherwise. The mutex must be
158   // locked by the current thread of execution, otherwise, the behavior is
159   // undefined.
160   void Unlock();
161 
162   // Tries to lock the given mutex. Returns whether the mutex was
163   // successfully locked.
164   bool TryLock() V8_WARN_UNUSED_RESULT;
165 
166  private:
167   // The implementation-defined native handle type.
168 #if V8_OS_POSIX
169   using NativeHandle = pthread_mutex_t;
170 #elif V8_OS_WIN
171   using NativeHandle = CRITICAL_SECTION;
172 #elif V8_OS_STARBOARD
173   using NativeHandle = starboard::RecursiveMutex;
174 #endif
175 
176   NativeHandle native_handle_;
177 #ifdef DEBUG
178   int level_;
179 #endif
180 };
181 
182 
183 // POD RecursiveMutex initialized lazily (i.e. the first time Pointer() is
184 // called).
185 // Usage:
186 //   static LazyRecursiveMutex my_mutex = LAZY_RECURSIVE_MUTEX_INITIALIZER;
187 //
188 //   void my_function() {
189 //     LockGuard<RecursiveMutex> guard(my_mutex.Pointer());
190 //     // Do something.
191 //   }
192 //
193 using LazyRecursiveMutex =
194     LazyStaticInstance<RecursiveMutex, DefaultConstructTrait<RecursiveMutex>,
195                        ThreadSafeInitOnceTrait>::type;
196 
197 #define LAZY_RECURSIVE_MUTEX_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
198 
199 // ----------------------------------------------------------------------------
200 // SharedMutex - a replacement for std::shared_mutex
201 //
202 // This class is a synchronization primitive that can be used to protect shared
203 // data from being simultaneously accessed by multiple threads. In contrast to
204 // other mutex types which facilitate exclusive access, a shared_mutex has two
205 // levels of access:
206 // - shared: several threads can share ownership of the same mutex.
207 // - exclusive: only one thread can own the mutex.
208 // Shared mutexes are usually used in situations when multiple readers can
209 // access the same resource at the same time without causing data races, but
210 // only one writer can do so.
211 // The SharedMutex class is non-copyable.
212 
213 class V8_BASE_EXPORT SharedMutex final {
214  public:
215   SharedMutex();
216   SharedMutex(const SharedMutex&) = delete;
217   SharedMutex& operator=(const SharedMutex&) = delete;
218   ~SharedMutex();
219 
220   // Acquires shared ownership of the {SharedMutex}. If another thread is
221   // holding the mutex in exclusive ownership, a call to {LockShared()} will
222   // block execution until shared ownership can be acquired.
223   // If {LockShared()} is called by a thread that already owns the mutex in any
224   // mode (exclusive or shared), the behavior is undefined.
225   void LockShared();
226 
227   // Locks the SharedMutex. If another thread has already locked the mutex, a
228   // call to {LockExclusive()} will block execution until the lock is acquired.
229   // If {LockExclusive()} is called by a thread that already owns the mutex in
230   // any mode (shared or exclusive), the behavior is undefined.
231   void LockExclusive();
232 
233   // Releases the {SharedMutex} from shared ownership by the calling thread.
234   // The mutex must be locked by the current thread of execution in shared mode,
235   // otherwise, the behavior is undefined.
236   void UnlockShared();
237 
238   // Unlocks the {SharedMutex}. It must be locked by the current thread of
239   // execution, otherwise, the behavior is undefined.
240   void UnlockExclusive();
241 
242   // Tries to lock the {SharedMutex} in shared mode. Returns immediately. On
243   // successful lock acquisition returns true, otherwise returns false.
244   // This function is allowed to fail spuriously and return false even if the
245   // mutex is not currenly exclusively locked by any other thread.
246   bool TryLockShared() V8_WARN_UNUSED_RESULT;
247 
248   // Tries to lock the {SharedMutex}. Returns immediately. On successful lock
249   // acquisition returns true, otherwise returns false.
250   // This function is allowed to fail spuriously and return false even if the
251   // mutex is not currently locked by any other thread.
252   // If try_lock is called by a thread that already owns the mutex in any mode
253   // (shared or exclusive), the behavior is undefined.
254   bool TryLockExclusive() V8_WARN_UNUSED_RESULT;
255 
256  private:
257   // The implementation-defined native handle type.
258 #if V8_OS_POSIX
259   using NativeHandle = pthread_rwlock_t;
260 #elif V8_OS_WIN
261   using NativeHandle = SRWLOCK;
262 #elif V8_OS_STARBOARD
263   using NativeHandle = starboard::RWLock;
264 #endif
265 
266   NativeHandle native_handle_;
267 };
268 
269 // -----------------------------------------------------------------------------
270 // LockGuard
271 //
272 // This class is a mutex wrapper that provides a convenient RAII-style mechanism
273 // for owning a mutex for the duration of a scoped block.
274 // When a LockGuard object is created, it attempts to take ownership of the
275 // mutex it is given. When control leaves the scope in which the LockGuard
276 // object was created, the LockGuard is destructed and the mutex is released.
277 // The LockGuard class is non-copyable.
278 
279 // Controls whether a LockGuard always requires a valid Mutex or will just
280 // ignore it if it's nullptr.
281 enum class NullBehavior { kRequireNotNull, kIgnoreIfNull };
282 
283 template <typename Mutex, NullBehavior Behavior = NullBehavior::kRequireNotNull>
284 class LockGuard final {
285  public:
LockGuard(Mutex * mutex)286   explicit LockGuard(Mutex* mutex) : mutex_(mutex) {
287     if (has_mutex()) mutex_->Lock();
288   }
289   LockGuard(const LockGuard&) = delete;
290   LockGuard& operator=(const LockGuard&) = delete;
~LockGuard()291   ~LockGuard() {
292     if (has_mutex()) mutex_->Unlock();
293   }
294 
295  private:
296   Mutex* const mutex_;
297 
has_mutex()298   bool V8_INLINE has_mutex() const {
299     DCHECK_IMPLIES(Behavior == NullBehavior::kRequireNotNull,
300                    mutex_ != nullptr);
301     return Behavior == NullBehavior::kRequireNotNull || mutex_ != nullptr;
302   }
303 };
304 
305 using MutexGuard = LockGuard<Mutex>;
306 using RecursiveMutexGuard = LockGuard<RecursiveMutex>;
307 
308 enum MutexSharedType : bool { kShared = true, kExclusive = false };
309 
310 template <MutexSharedType kIsShared,
311           NullBehavior Behavior = NullBehavior::kRequireNotNull>
312 class SharedMutexGuard final {
313  public:
SharedMutexGuard(SharedMutex * mutex)314   explicit SharedMutexGuard(SharedMutex* mutex) : mutex_(mutex) {
315     if (!has_mutex()) return;
316     if (kIsShared) {
317       mutex_->LockShared();
318     } else {
319       mutex_->LockExclusive();
320     }
321   }
322   SharedMutexGuard(const SharedMutexGuard&) = delete;
323   SharedMutexGuard& operator=(const SharedMutexGuard&) = delete;
~SharedMutexGuard()324   ~SharedMutexGuard() {
325     if (!has_mutex()) return;
326     if (kIsShared) {
327       mutex_->UnlockShared();
328     } else {
329       mutex_->UnlockExclusive();
330     }
331   }
332 
333  private:
334   SharedMutex* const mutex_;
335 
has_mutex()336   bool V8_INLINE has_mutex() const {
337     DCHECK_IMPLIES(Behavior == NullBehavior::kRequireNotNull,
338                    mutex_ != nullptr);
339     return Behavior == NullBehavior::kRequireNotNull || mutex_ != nullptr;
340   }
341 };
342 
343 }  // namespace base
344 }  // namespace v8
345 
346 #endif  // V8_BASE_PLATFORM_MUTEX_H_
347