• 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 #include "src/base/platform/mutex.h"
6 
7 #include <errno.h>
8 
9 #if DEBUG
10 #include <unordered_set>
11 #endif  // DEBUG
12 
13 #if V8_OS_WIN
14 #include <windows.h>
15 #endif
16 
17 namespace v8 {
18 namespace base {
19 
20 #if DEBUG
21 namespace {
22 // Used for asserts to guarantee we are not re-locking a mutex on the same
23 // thread. If this thread has only one held shared mutex (common case), we use
24 // {single_held_shared_mutex}. If it has more than one we allocate a set for it.
25 // Said set has to manually be constructed and destroyed.
26 thread_local base::SharedMutex* single_held_shared_mutex = nullptr;
27 using TSet = std::unordered_set<base::SharedMutex*>;
28 thread_local TSet* held_shared_mutexes = nullptr;
29 
30 // Returns true iff {shared_mutex} is not a held mutex.
SharedMutexNotHeld(SharedMutex * shared_mutex)31 bool SharedMutexNotHeld(SharedMutex* shared_mutex) {
32   DCHECK_NOT_NULL(shared_mutex);
33   return single_held_shared_mutex != shared_mutex &&
34          (!held_shared_mutexes ||
35           held_shared_mutexes->count(shared_mutex) == 0);
36 }
37 
38 // Tries to hold {shared_mutex}. Returns true iff it hadn't been held prior to
39 // this function call.
TryHoldSharedMutex(SharedMutex * shared_mutex)40 bool TryHoldSharedMutex(SharedMutex* shared_mutex) {
41   DCHECK_NOT_NULL(shared_mutex);
42   if (single_held_shared_mutex) {
43     if (shared_mutex == single_held_shared_mutex) {
44       return false;
45     }
46     DCHECK_NULL(held_shared_mutexes);
47     held_shared_mutexes = new TSet({single_held_shared_mutex, shared_mutex});
48     single_held_shared_mutex = nullptr;
49     return true;
50   } else if (held_shared_mutexes) {
51     return held_shared_mutexes->insert(shared_mutex).second;
52   } else {
53     DCHECK_NULL(single_held_shared_mutex);
54     single_held_shared_mutex = shared_mutex;
55     return true;
56   }
57 }
58 
59 // Tries to release {shared_mutex}. Returns true iff it had been held prior to
60 // this function call.
TryReleaseSharedMutex(SharedMutex * shared_mutex)61 bool TryReleaseSharedMutex(SharedMutex* shared_mutex) {
62   DCHECK_NOT_NULL(shared_mutex);
63   if (single_held_shared_mutex == shared_mutex) {
64     single_held_shared_mutex = nullptr;
65     return true;
66   }
67   if (held_shared_mutexes && held_shared_mutexes->erase(shared_mutex)) {
68     if (held_shared_mutexes->empty()) {
69       delete held_shared_mutexes;
70       held_shared_mutexes = nullptr;
71     }
72     return true;
73   }
74   return false;
75 }
76 }  // namespace
77 #endif  // DEBUG
78 
79 #if V8_OS_POSIX
80 
InitializeNativeHandle(pthread_mutex_t * mutex)81 static V8_INLINE void InitializeNativeHandle(pthread_mutex_t* mutex) {
82   int result;
83 #if defined(DEBUG)
84   // Use an error checking mutex in debug mode.
85   pthread_mutexattr_t attr;
86   result = pthread_mutexattr_init(&attr);
87   DCHECK_EQ(0, result);
88   result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
89   DCHECK_EQ(0, result);
90   result = pthread_mutex_init(mutex, &attr);
91   DCHECK_EQ(0, result);
92   result = pthread_mutexattr_destroy(&attr);
93 #else
94   // Use a fast mutex (default attributes).
95   result = pthread_mutex_init(mutex, nullptr);
96 #endif  // defined(DEBUG)
97   DCHECK_EQ(0, result);
98   USE(result);
99 }
100 
101 
InitializeRecursiveNativeHandle(pthread_mutex_t * mutex)102 static V8_INLINE void InitializeRecursiveNativeHandle(pthread_mutex_t* mutex) {
103   pthread_mutexattr_t attr;
104   int result = pthread_mutexattr_init(&attr);
105   DCHECK_EQ(0, result);
106   result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
107   DCHECK_EQ(0, result);
108   result = pthread_mutex_init(mutex, &attr);
109   DCHECK_EQ(0, result);
110   result = pthread_mutexattr_destroy(&attr);
111   DCHECK_EQ(0, result);
112   USE(result);
113 }
114 
115 
DestroyNativeHandle(pthread_mutex_t * mutex)116 static V8_INLINE void DestroyNativeHandle(pthread_mutex_t* mutex) {
117   int result = pthread_mutex_destroy(mutex);
118   DCHECK_EQ(0, result);
119   USE(result);
120 }
121 
122 
LockNativeHandle(pthread_mutex_t * mutex)123 static V8_INLINE void LockNativeHandle(pthread_mutex_t* mutex) {
124   int result = pthread_mutex_lock(mutex);
125   DCHECK_EQ(0, result);
126   USE(result);
127 }
128 
129 
UnlockNativeHandle(pthread_mutex_t * mutex)130 static V8_INLINE void UnlockNativeHandle(pthread_mutex_t* mutex) {
131   int result = pthread_mutex_unlock(mutex);
132   DCHECK_EQ(0, result);
133   USE(result);
134 }
135 
136 
TryLockNativeHandle(pthread_mutex_t * mutex)137 static V8_INLINE bool TryLockNativeHandle(pthread_mutex_t* mutex) {
138   int result = pthread_mutex_trylock(mutex);
139   if (result == EBUSY) {
140     return false;
141   }
142   DCHECK_EQ(0, result);
143   return true;
144 }
145 
146 
Mutex()147 Mutex::Mutex() {
148   InitializeNativeHandle(&native_handle_);
149 #ifdef DEBUG
150   level_ = 0;
151 #endif
152 }
153 
154 
~Mutex()155 Mutex::~Mutex() {
156   DestroyNativeHandle(&native_handle_);
157   DCHECK_EQ(0, level_);
158 }
159 
160 
Lock()161 void Mutex::Lock() {
162   LockNativeHandle(&native_handle_);
163   AssertUnheldAndMark();
164 }
165 
166 
Unlock()167 void Mutex::Unlock() {
168   AssertHeldAndUnmark();
169   UnlockNativeHandle(&native_handle_);
170 }
171 
172 
TryLock()173 bool Mutex::TryLock() {
174   if (!TryLockNativeHandle(&native_handle_)) {
175     return false;
176   }
177   AssertUnheldAndMark();
178   return true;
179 }
180 
181 
RecursiveMutex()182 RecursiveMutex::RecursiveMutex() {
183   InitializeRecursiveNativeHandle(&native_handle_);
184 #ifdef DEBUG
185   level_ = 0;
186 #endif
187 }
188 
189 
~RecursiveMutex()190 RecursiveMutex::~RecursiveMutex() {
191   DestroyNativeHandle(&native_handle_);
192   DCHECK_EQ(0, level_);
193 }
194 
195 
Lock()196 void RecursiveMutex::Lock() {
197   LockNativeHandle(&native_handle_);
198 #ifdef DEBUG
199   DCHECK_LE(0, level_);
200   level_++;
201 #endif
202 }
203 
204 
Unlock()205 void RecursiveMutex::Unlock() {
206 #ifdef DEBUG
207   DCHECK_LT(0, level_);
208   level_--;
209 #endif
210   UnlockNativeHandle(&native_handle_);
211 }
212 
213 
TryLock()214 bool RecursiveMutex::TryLock() {
215   if (!TryLockNativeHandle(&native_handle_)) {
216     return false;
217   }
218 #ifdef DEBUG
219   DCHECK_LE(0, level_);
220   level_++;
221 #endif
222   return true;
223 }
224 
225 #if V8_OS_DARWIN
226 
SharedMutex()227 SharedMutex::SharedMutex() { InitializeNativeHandle(&native_handle_); }
228 
~SharedMutex()229 SharedMutex::~SharedMutex() { DestroyNativeHandle(&native_handle_); }
230 
LockShared()231 void SharedMutex::LockShared() { LockExclusive(); }
232 
LockExclusive()233 void SharedMutex::LockExclusive() {
234   DCHECK(TryHoldSharedMutex(this));
235   LockNativeHandle(&native_handle_);
236 }
237 
UnlockShared()238 void SharedMutex::UnlockShared() { UnlockExclusive(); }
239 
UnlockExclusive()240 void SharedMutex::UnlockExclusive() {
241   DCHECK(TryReleaseSharedMutex(this));
242   UnlockNativeHandle(&native_handle_);
243 }
244 
TryLockShared()245 bool SharedMutex::TryLockShared() { return TryLockExclusive(); }
246 
TryLockExclusive()247 bool SharedMutex::TryLockExclusive() {
248   DCHECK(SharedMutexNotHeld(this));
249   if (!TryLockNativeHandle(&native_handle_)) return false;
250   DCHECK(TryHoldSharedMutex(this));
251   return true;
252 }
253 
254 #else  // !V8_OS_DARWIN
255 
SharedMutex()256 SharedMutex::SharedMutex() { pthread_rwlock_init(&native_handle_, nullptr); }
257 
~SharedMutex()258 SharedMutex::~SharedMutex() {
259   int result = pthread_rwlock_destroy(&native_handle_);
260   DCHECK_EQ(0, result);
261   USE(result);
262 }
263 
LockShared()264 void SharedMutex::LockShared() {
265   DCHECK(TryHoldSharedMutex(this));
266   int result = pthread_rwlock_rdlock(&native_handle_);
267   DCHECK_EQ(0, result);
268   USE(result);
269 }
270 
LockExclusive()271 void SharedMutex::LockExclusive() {
272   DCHECK(TryHoldSharedMutex(this));
273   int result = pthread_rwlock_wrlock(&native_handle_);
274   DCHECK_EQ(0, result);
275   USE(result);
276 }
277 
UnlockShared()278 void SharedMutex::UnlockShared() {
279   DCHECK(TryReleaseSharedMutex(this));
280   int result = pthread_rwlock_unlock(&native_handle_);
281   DCHECK_EQ(0, result);
282   USE(result);
283 }
284 
UnlockExclusive()285 void SharedMutex::UnlockExclusive() {
286   // Same code as {UnlockShared} on POSIX.
287   UnlockShared();
288 }
289 
TryLockShared()290 bool SharedMutex::TryLockShared() {
291   DCHECK(SharedMutexNotHeld(this));
292   bool result = pthread_rwlock_tryrdlock(&native_handle_) == 0;
293   if (result) DCHECK(TryHoldSharedMutex(this));
294   return result;
295 }
296 
TryLockExclusive()297 bool SharedMutex::TryLockExclusive() {
298   DCHECK(SharedMutexNotHeld(this));
299   bool result = pthread_rwlock_trywrlock(&native_handle_) == 0;
300   if (result) DCHECK(TryHoldSharedMutex(this));
301   return result;
302 }
303 
304 #endif  // !V8_OS_DARWIN
305 
306 #elif V8_OS_WIN
307 
Mutex()308 Mutex::Mutex() : native_handle_(SRWLOCK_INIT) {
309 #ifdef DEBUG
310   level_ = 0;
311 #endif
312 }
313 
314 
~Mutex()315 Mutex::~Mutex() {
316   DCHECK_EQ(0, level_);
317 }
318 
319 
Lock()320 void Mutex::Lock() {
321   AcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
322   AssertUnheldAndMark();
323 }
324 
325 
Unlock()326 void Mutex::Unlock() {
327   AssertHeldAndUnmark();
328   ReleaseSRWLockExclusive(V8ToWindowsType(&native_handle_));
329 }
330 
331 
TryLock()332 bool Mutex::TryLock() {
333   if (!TryAcquireSRWLockExclusive(V8ToWindowsType(&native_handle_))) {
334     return false;
335   }
336   AssertUnheldAndMark();
337   return true;
338 }
339 
340 
RecursiveMutex()341 RecursiveMutex::RecursiveMutex() {
342   InitializeCriticalSection(V8ToWindowsType(&native_handle_));
343 #ifdef DEBUG
344   level_ = 0;
345 #endif
346 }
347 
348 
~RecursiveMutex()349 RecursiveMutex::~RecursiveMutex() {
350   DeleteCriticalSection(V8ToWindowsType(&native_handle_));
351   DCHECK_EQ(0, level_);
352 }
353 
354 
Lock()355 void RecursiveMutex::Lock() {
356   EnterCriticalSection(V8ToWindowsType(&native_handle_));
357 #ifdef DEBUG
358   DCHECK_LE(0, level_);
359   level_++;
360 #endif
361 }
362 
363 
Unlock()364 void RecursiveMutex::Unlock() {
365 #ifdef DEBUG
366   DCHECK_LT(0, level_);
367   level_--;
368 #endif
369   LeaveCriticalSection(V8ToWindowsType(&native_handle_));
370 }
371 
372 
TryLock()373 bool RecursiveMutex::TryLock() {
374   if (!TryEnterCriticalSection(V8ToWindowsType(&native_handle_))) {
375     return false;
376   }
377 #ifdef DEBUG
378   DCHECK_LE(0, level_);
379   level_++;
380 #endif
381   return true;
382 }
383 
SharedMutex()384 SharedMutex::SharedMutex() : native_handle_(SRWLOCK_INIT) {}
385 
~SharedMutex()386 SharedMutex::~SharedMutex() {}
387 
LockShared()388 void SharedMutex::LockShared() {
389   DCHECK(TryHoldSharedMutex(this));
390   AcquireSRWLockShared(V8ToWindowsType(&native_handle_));
391 }
392 
LockExclusive()393 void SharedMutex::LockExclusive() {
394   DCHECK(TryHoldSharedMutex(this));
395   AcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
396 }
397 
UnlockShared()398 void SharedMutex::UnlockShared() {
399   DCHECK(TryReleaseSharedMutex(this));
400   ReleaseSRWLockShared(V8ToWindowsType(&native_handle_));
401 }
402 
UnlockExclusive()403 void SharedMutex::UnlockExclusive() {
404   DCHECK(TryReleaseSharedMutex(this));
405   ReleaseSRWLockExclusive(V8ToWindowsType(&native_handle_));
406 }
407 
TryLockShared()408 bool SharedMutex::TryLockShared() {
409   DCHECK(SharedMutexNotHeld(this));
410   bool result = TryAcquireSRWLockShared(V8ToWindowsType(&native_handle_));
411   if (result) DCHECK(TryHoldSharedMutex(this));
412   return result;
413 }
414 
TryLockExclusive()415 bool SharedMutex::TryLockExclusive() {
416   DCHECK(SharedMutexNotHeld(this));
417   bool result = TryAcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
418   if (result) DCHECK(TryHoldSharedMutex(this));
419   return result;
420 }
421 
422 #elif V8_OS_STARBOARD
423 
Mutex()424 Mutex::Mutex() { SbMutexCreate(&native_handle_); }
425 
~Mutex()426 Mutex::~Mutex() { SbMutexDestroy(&native_handle_); }
427 
Lock()428 void Mutex::Lock() { SbMutexAcquire(&native_handle_); }
429 
Unlock()430 void Mutex::Unlock() { SbMutexRelease(&native_handle_); }
431 
RecursiveMutex()432 RecursiveMutex::RecursiveMutex() {}
433 
~RecursiveMutex()434 RecursiveMutex::~RecursiveMutex() {}
435 
Lock()436 void RecursiveMutex::Lock() { native_handle_.Acquire(); }
437 
Unlock()438 void RecursiveMutex::Unlock() { native_handle_.Release(); }
439 
TryLock()440 bool RecursiveMutex::TryLock() { return native_handle_.AcquireTry(); }
441 
442 SharedMutex::SharedMutex() = default;
443 
444 SharedMutex::~SharedMutex() = default;
445 
LockShared()446 void SharedMutex::LockShared() {
447   DCHECK(TryHoldSharedMutex(this));
448   native_handle_.AcquireReadLock();
449 }
450 
LockExclusive()451 void SharedMutex::LockExclusive() {
452   DCHECK(TryHoldSharedMutex(this));
453   native_handle_.AcquireWriteLock();
454 }
455 
UnlockShared()456 void SharedMutex::UnlockShared() {
457   DCHECK(TryReleaseSharedMutex(this));
458   native_handle_.ReleaseReadLock();
459 }
460 
UnlockExclusive()461 void SharedMutex::UnlockExclusive() {
462   DCHECK(TryReleaseSharedMutex(this));
463   native_handle_.ReleaseWriteLock();
464 }
465 
TryLockShared()466 bool SharedMutex::TryLockShared() {
467   DCHECK(SharedMutexNotHeld(this));
468   return false;
469 }
470 
TryLockExclusive()471 bool SharedMutex::TryLockExclusive() {
472   DCHECK(SharedMutexNotHeld(this));
473   return false;
474 }
475 #endif  // V8_OS_STARBOARD
476 
477 }  // namespace base
478 }  // namespace v8
479