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