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