• 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 namespace v8 {
20 namespace base {
21 
22 // ----------------------------------------------------------------------------
23 // Mutex
24 //
25 // This class is a synchronization primitive that can be used to protect shared
26 // data from being simultaneously accessed by multiple threads. A mutex offers
27 // exclusive, non-recursive ownership semantics:
28 // - A calling thread owns a mutex from the time that it successfully calls
29 //   either |Lock()| or |TryLock()| until it calls |Unlock()|.
30 // - When a thread owns a mutex, all other threads will block (for calls to
31 //   |Lock()|) or receive a |false| return value (for |TryLock()|) if they
32 //   attempt to claim ownership of the mutex.
33 // A calling thread must not own the mutex prior to calling |Lock()| or
34 // |TryLock()|. The behavior of a program is undefined if a mutex is destroyed
35 // while still owned by some thread. The Mutex class is non-copyable.
36 
37 class V8_BASE_EXPORT Mutex final {
38  public:
39   Mutex();
40   ~Mutex();
41 
42   // Locks the given mutex. If the mutex is currently unlocked, it becomes
43   // locked and owned by the calling thread, and immediately. If the mutex
44   // is already locked by another thread, suspends the calling thread until
45   // the mutex is unlocked.
46   void Lock();
47 
48   // Unlocks the given mutex. The mutex is assumed to be locked and owned by
49   // the calling thread on entrance.
50   void Unlock();
51 
52   // Tries to lock the given mutex. Returns whether the mutex was
53   // successfully locked.
54   bool TryLock() V8_WARN_UNUSED_RESULT;
55 
56   // The implementation-defined native handle type.
57 #if V8_OS_POSIX
58   typedef pthread_mutex_t NativeHandle;
59 #elif V8_OS_WIN
60   typedef SRWLOCK NativeHandle;
61 #endif
62 
native_handle()63   NativeHandle& native_handle() {
64     return native_handle_;
65   }
native_handle()66   const NativeHandle& native_handle() const {
67     return native_handle_;
68   }
69 
70  private:
71   NativeHandle native_handle_;
72 #ifdef DEBUG
73   int level_;
74 #endif
75 
AssertHeldAndUnmark()76   V8_INLINE void AssertHeldAndUnmark() {
77 #ifdef DEBUG
78     DCHECK_EQ(1, level_);
79     level_--;
80 #endif
81   }
82 
AssertUnheldAndMark()83   V8_INLINE void AssertUnheldAndMark() {
84 #ifdef DEBUG
85     DCHECK_EQ(0, level_);
86     level_++;
87 #endif
88   }
89 
90   friend class ConditionVariable;
91 
92   DISALLOW_COPY_AND_ASSIGN(Mutex);
93 };
94 
95 
96 // POD Mutex initialized lazily (i.e. the first time Pointer() is called).
97 // Usage:
98 //   static LazyMutex my_mutex = LAZY_MUTEX_INITIALIZER;
99 //
100 //   void my_function() {
101 //     LockGuard<Mutex> guard(my_mutex.Pointer());
102 //     // Do something.
103 //   }
104 //
105 typedef LazyStaticInstance<Mutex, DefaultConstructTrait<Mutex>,
106                            ThreadSafeInitOnceTrait>::type LazyMutex;
107 
108 #define LAZY_MUTEX_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
109 
110 
111 // -----------------------------------------------------------------------------
112 // RecursiveMutex
113 //
114 // This class is a synchronization primitive that can be used to protect shared
115 // data from being simultaneously accessed by multiple threads. A recursive
116 // mutex offers exclusive, recursive ownership semantics:
117 // - A calling thread owns a recursive mutex for a period of time that starts
118 //   when it successfully calls either |Lock()| or |TryLock()|. During this
119 //   period, the thread may make additional calls to |Lock()| or |TryLock()|.
120 //   The period of ownership ends when the thread makes a matching number of
121 //   calls to |Unlock()|.
122 // - When a thread owns a recursive mutex, all other threads will block (for
123 //   calls to |Lock()|) or receive a |false| return value (for |TryLock()|) if
124 //   they attempt to claim ownership of the recursive mutex.
125 // - The maximum number of times that a recursive mutex may be locked is
126 //   unspecified, but after that number is reached, calls to |Lock()| will
127 //   probably abort the process and calls to |TryLock()| return false.
128 // The behavior of a program is undefined if a recursive mutex is destroyed
129 // while still owned by some thread. The RecursiveMutex class is non-copyable.
130 
131 class V8_BASE_EXPORT RecursiveMutex final {
132  public:
133   RecursiveMutex();
134   ~RecursiveMutex();
135 
136   // Locks the mutex. If another thread has already locked the mutex, a call to
137   // |Lock()| will block execution until the lock is acquired. A thread may call
138   // |Lock()| on a recursive mutex repeatedly. Ownership will only be released
139   // after the thread makes a matching number of calls to |Unlock()|.
140   // The behavior is undefined if the mutex is not unlocked before being
141   // destroyed, i.e. some thread still owns it.
142   void Lock();
143 
144   // Unlocks the mutex if its level of ownership is 1 (there was exactly one
145   // more call to |Lock()| than there were calls to unlock() made by this
146   // thread), reduces the level of ownership by 1 otherwise. The mutex must be
147   // locked by the current thread of execution, otherwise, the behavior is
148   // undefined.
149   void Unlock();
150 
151   // Tries to lock the given mutex. Returns whether the mutex was
152   // successfully locked.
153   bool TryLock() V8_WARN_UNUSED_RESULT;
154 
155   // The implementation-defined native handle type.
156 #if V8_OS_POSIX
157   typedef pthread_mutex_t NativeHandle;
158 #elif V8_OS_WIN
159   typedef CRITICAL_SECTION NativeHandle;
160 #endif
161 
native_handle()162   NativeHandle& native_handle() {
163     return native_handle_;
164   }
native_handle()165   const NativeHandle& native_handle() const {
166     return native_handle_;
167   }
168 
169  private:
170   NativeHandle native_handle_;
171 #ifdef DEBUG
172   int level_;
173 #endif
174 
175   DISALLOW_COPY_AND_ASSIGN(RecursiveMutex);
176 };
177 
178 
179 // POD RecursiveMutex initialized lazily (i.e. the first time Pointer() is
180 // called).
181 // Usage:
182 //   static LazyRecursiveMutex my_mutex = LAZY_RECURSIVE_MUTEX_INITIALIZER;
183 //
184 //   void my_function() {
185 //     LockGuard<RecursiveMutex> guard(my_mutex.Pointer());
186 //     // Do something.
187 //   }
188 //
189 typedef LazyStaticInstance<RecursiveMutex,
190                            DefaultConstructTrait<RecursiveMutex>,
191                            ThreadSafeInitOnceTrait>::type LazyRecursiveMutex;
192 
193 #define LAZY_RECURSIVE_MUTEX_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
194 
195 
196 // -----------------------------------------------------------------------------
197 // LockGuard
198 //
199 // This class is a mutex wrapper that provides a convenient RAII-style mechanism
200 // for owning a mutex for the duration of a scoped block.
201 // When a LockGuard object is created, it attempts to take ownership of the
202 // mutex it is given. When control leaves the scope in which the LockGuard
203 // object was created, the LockGuard is destructed and the mutex is released.
204 // The LockGuard class is non-copyable.
205 
206 // Controls whether a LockGuard always requires a valid Mutex or will just
207 // ignore it if it's nullptr.
208 enum class NullBehavior { kRequireNotNull, kIgnoreIfNull };
209 
210 template <typename Mutex, NullBehavior Behavior = NullBehavior::kRequireNotNull>
211 class LockGuard final {
212  public:
LockGuard(Mutex * mutex)213   explicit LockGuard(Mutex* mutex) : mutex_(mutex) {
214     if (Behavior == NullBehavior::kRequireNotNull || mutex_ != nullptr) {
215       mutex_->Lock();
216     }
217   }
~LockGuard()218   ~LockGuard() {
219     if (mutex_ != nullptr) mutex_->Unlock();
220   }
221 
222  private:
223   Mutex* mutex_;
224 
225   DISALLOW_COPY_AND_ASSIGN(LockGuard);
226 };
227 
228 }  // namespace base
229 }  // namespace v8
230 
231 #endif  // V8_BASE_PLATFORM_MUTEX_H_
232