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