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