mutex.cc 10.7 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/base/platform/mutex.h"
6

7
#include <errno.h>
8

9 10 11 12
#if DEBUG
#include <unordered_set>
#endif  // DEBUG

13 14 15 16
#if V8_OS_WIN
#include <windows.h>
#endif

17
namespace v8 {
18
namespace base {
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
#if DEBUG
namespace {
// Used for asserts to guarantee we are not re-locking a mutex on the same
// thread. If this thread has only one held shared mutex (common case), we use
// {single_held_shared_mutex}. If it has more than one we allocate a set for it.
// Said set has to manually be constructed and destroyed.
thread_local base::SharedMutex* single_held_shared_mutex = nullptr;
using TSet = std::unordered_set<base::SharedMutex*>;
thread_local TSet* held_shared_mutexes = nullptr;

// Returns true iff {shared_mutex} is not a held mutex.
bool SharedMutexNotHeld(SharedMutex* shared_mutex) {
  DCHECK_NOT_NULL(shared_mutex);
  return single_held_shared_mutex != shared_mutex &&
         (!held_shared_mutexes ||
          held_shared_mutexes->count(shared_mutex) == 0);
}

// Tries to hold {shared_mutex}. Returns true iff it hadn't been held prior to
// this function call.
bool TryHoldSharedMutex(SharedMutex* shared_mutex) {
  DCHECK_NOT_NULL(shared_mutex);
  if (single_held_shared_mutex) {
    if (shared_mutex == single_held_shared_mutex) {
      return false;
    }
    DCHECK_NULL(held_shared_mutexes);
    held_shared_mutexes = new TSet({single_held_shared_mutex, shared_mutex});
    single_held_shared_mutex = nullptr;
    return true;
  } else if (held_shared_mutexes) {
    return held_shared_mutexes->insert(shared_mutex).second;
  } else {
    DCHECK_NULL(single_held_shared_mutex);
    single_held_shared_mutex = shared_mutex;
    return true;
  }
}

// Tries to release {shared_mutex}. Returns true iff it had been held prior to
// this function call.
bool TryReleaseSharedMutex(SharedMutex* shared_mutex) {
  DCHECK_NOT_NULL(shared_mutex);
  if (single_held_shared_mutex == shared_mutex) {
    single_held_shared_mutex = nullptr;
    return true;
  }
  if (held_shared_mutexes && held_shared_mutexes->erase(shared_mutex)) {
    if (held_shared_mutexes->empty()) {
      delete held_shared_mutexes;
      held_shared_mutexes = nullptr;
    }
    return true;
  }
  return false;
}
}  // namespace
#endif  // DEBUG

79 80
#if V8_OS_POSIX

81
static V8_INLINE void InitializeNativeHandle(pthread_mutex_t* mutex) {
82 83 84 85 86
  int result;
#if defined(DEBUG)
  // Use an error checking mutex in debug mode.
  pthread_mutexattr_t attr;
  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 93 94
  result = pthread_mutexattr_destroy(&attr);
#else
  // Use a fast mutex (default attributes).
95
  result = pthread_mutex_init(mutex, nullptr);
96
#endif  // defined(DEBUG)
97
  DCHECK_EQ(0, result);
98 99 100 101
  USE(result);
}


102
static V8_INLINE void InitializeRecursiveNativeHandle(pthread_mutex_t* mutex) {
103 104
  pthread_mutexattr_t attr;
  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 113 114 115
  USE(result);
}


116
static V8_INLINE void DestroyNativeHandle(pthread_mutex_t* mutex) {
117
  int result = pthread_mutex_destroy(mutex);
118
  DCHECK_EQ(0, result);
119 120 121 122
  USE(result);
}


123
static V8_INLINE void LockNativeHandle(pthread_mutex_t* mutex) {
124
  int result = pthread_mutex_lock(mutex);
125
  DCHECK_EQ(0, result);
126 127 128 129
  USE(result);
}


130
static V8_INLINE void UnlockNativeHandle(pthread_mutex_t* mutex) {
131
  int result = pthread_mutex_unlock(mutex);
132
  DCHECK_EQ(0, result);
133 134 135 136
  USE(result);
}


137
static V8_INLINE bool TryLockNativeHandle(pthread_mutex_t* mutex) {
138 139 140 141
  int result = pthread_mutex_trylock(mutex);
  if (result == EBUSY) {
    return false;
  }
142
  DCHECK_EQ(0, result);
143 144 145 146
  return true;
}


147 148 149 150 151
Mutex::Mutex() {
  InitializeNativeHandle(&native_handle_);
#ifdef DEBUG
  level_ = 0;
#endif
152 153 154
}


155 156 157
Mutex::~Mutex() {
  DestroyNativeHandle(&native_handle_);
  DCHECK_EQ(0, level_);
158 159 160
}


161 162 163
void Mutex::Lock() {
  LockNativeHandle(&native_handle_);
  AssertUnheldAndMark();
164 165 166
}


167 168 169
void Mutex::Unlock() {
  AssertHeldAndUnmark();
  UnlockNativeHandle(&native_handle_);
170 171 172
}


173 174 175 176 177 178
bool Mutex::TryLock() {
  if (!TryLockNativeHandle(&native_handle_)) {
    return false;
  }
  AssertUnheldAndMark();
  return true;
179 180 181
}


182 183 184 185 186
RecursiveMutex::RecursiveMutex() {
  InitializeRecursiveNativeHandle(&native_handle_);
#ifdef DEBUG
  level_ = 0;
#endif
187 188 189
}


190 191 192 193
RecursiveMutex::~RecursiveMutex() {
  DestroyNativeHandle(&native_handle_);
  DCHECK_EQ(0, level_);
}
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

void RecursiveMutex::Lock() {
  LockNativeHandle(&native_handle_);
#ifdef DEBUG
  DCHECK_LE(0, level_);
  level_++;
#endif
}


void RecursiveMutex::Unlock() {
#ifdef DEBUG
  DCHECK_LT(0, level_);
  level_--;
#endif
  UnlockNativeHandle(&native_handle_);
}


bool RecursiveMutex::TryLock() {
  if (!TryLockNativeHandle(&native_handle_)) {
    return false;
  }
#ifdef DEBUG
  DCHECK_LE(0, level_);
  level_++;
#endif
  return true;
}

225
#if V8_OS_DARWIN
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

SharedMutex::SharedMutex() { InitializeNativeHandle(&native_handle_); }

SharedMutex::~SharedMutex() { DestroyNativeHandle(&native_handle_); }

void SharedMutex::LockShared() { LockExclusive(); }

void SharedMutex::LockExclusive() {
  DCHECK(TryHoldSharedMutex(this));
  LockNativeHandle(&native_handle_);
}

void SharedMutex::UnlockShared() { UnlockExclusive(); }

void SharedMutex::UnlockExclusive() {
  DCHECK(TryReleaseSharedMutex(this));
  UnlockNativeHandle(&native_handle_);
}

bool SharedMutex::TryLockShared() { return TryLockExclusive(); }

bool SharedMutex::TryLockExclusive() {
  DCHECK(SharedMutexNotHeld(this));
  if (!TryLockNativeHandle(&native_handle_)) return false;
  DCHECK(TryHoldSharedMutex(this));
  return true;
}

254
#else  // !V8_OS_DARWIN
255

256 257 258 259 260 261 262 263 264
SharedMutex::SharedMutex() { pthread_rwlock_init(&native_handle_, nullptr); }

SharedMutex::~SharedMutex() {
  int result = pthread_rwlock_destroy(&native_handle_);
  DCHECK_EQ(0, result);
  USE(result);
}

void SharedMutex::LockShared() {
265
  DCHECK(TryHoldSharedMutex(this));
266 267 268 269 270 271
  int result = pthread_rwlock_rdlock(&native_handle_);
  DCHECK_EQ(0, result);
  USE(result);
}

void SharedMutex::LockExclusive() {
272
  DCHECK(TryHoldSharedMutex(this));
273 274 275 276 277 278
  int result = pthread_rwlock_wrlock(&native_handle_);
  DCHECK_EQ(0, result);
  USE(result);
}

void SharedMutex::UnlockShared() {
279
  DCHECK(TryReleaseSharedMutex(this));
280 281 282 283 284 285 286 287 288 289 290
  int result = pthread_rwlock_unlock(&native_handle_);
  DCHECK_EQ(0, result);
  USE(result);
}

void SharedMutex::UnlockExclusive() {
  // Same code as {UnlockShared} on POSIX.
  UnlockShared();
}

bool SharedMutex::TryLockShared() {
291 292 293 294
  DCHECK(SharedMutexNotHeld(this));
  bool result = pthread_rwlock_tryrdlock(&native_handle_) == 0;
  if (result) DCHECK(TryHoldSharedMutex(this));
  return result;
295 296 297
}

bool SharedMutex::TryLockExclusive() {
298 299 300 301
  DCHECK(SharedMutexNotHeld(this));
  bool result = pthread_rwlock_trywrlock(&native_handle_) == 0;
  if (result) DCHECK(TryHoldSharedMutex(this));
  return result;
302 303
}

304
#endif  // !V8_OS_DARWIN
305

306 307 308
#elif V8_OS_WIN

Mutex::Mutex() : native_handle_(SRWLOCK_INIT) {
309 310 311 312 313 314 315
#ifdef DEBUG
  level_ = 0;
#endif
}


Mutex::~Mutex() {
316
  DCHECK_EQ(0, level_);
317 318 319 320
}


void Mutex::Lock() {
321
  AcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
322
  AssertUnheldAndMark();
323 324 325 326
}


void Mutex::Unlock() {
327
  AssertHeldAndUnmark();
328
  ReleaseSRWLockExclusive(V8ToWindowsType(&native_handle_));
329 330 331 332
}


bool Mutex::TryLock() {
333
  if (!TryAcquireSRWLockExclusive(V8ToWindowsType(&native_handle_))) {
334 335
    return false;
  }
336
  AssertUnheldAndMark();
337 338 339 340 341
  return true;
}


RecursiveMutex::RecursiveMutex() {
342
  InitializeCriticalSection(V8ToWindowsType(&native_handle_));
343 344 345 346 347 348 349
#ifdef DEBUG
  level_ = 0;
#endif
}


RecursiveMutex::~RecursiveMutex() {
350
  DeleteCriticalSection(V8ToWindowsType(&native_handle_));
351
  DCHECK_EQ(0, level_);
352 353 354 355
}


void RecursiveMutex::Lock() {
356
  EnterCriticalSection(V8ToWindowsType(&native_handle_));
357
#ifdef DEBUG
358
  DCHECK_LE(0, level_);
359 360 361 362 363 364 365
  level_++;
#endif
}


void RecursiveMutex::Unlock() {
#ifdef DEBUG
366
  DCHECK_LT(0, level_);
367 368
  level_--;
#endif
369
  LeaveCriticalSection(V8ToWindowsType(&native_handle_));
370 371 372 373
}


bool RecursiveMutex::TryLock() {
374
  if (!TryEnterCriticalSection(V8ToWindowsType(&native_handle_))) {
375 376 377
    return false;
  }
#ifdef DEBUG
378
  DCHECK_LE(0, level_);
379 380 381 382 383
  level_++;
#endif
  return true;
}

384 385 386 387
SharedMutex::SharedMutex() : native_handle_(SRWLOCK_INIT) {}

SharedMutex::~SharedMutex() {}

388 389
void SharedMutex::LockShared() {
  DCHECK(TryHoldSharedMutex(this));
390
  AcquireSRWLockShared(V8ToWindowsType(&native_handle_));
391
}
392

393 394
void SharedMutex::LockExclusive() {
  DCHECK(TryHoldSharedMutex(this));
395
  AcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
396
}
397

398 399
void SharedMutex::UnlockShared() {
  DCHECK(TryReleaseSharedMutex(this));
400
  ReleaseSRWLockShared(V8ToWindowsType(&native_handle_));
401
}
402 403

void SharedMutex::UnlockExclusive() {
404
  DCHECK(TryReleaseSharedMutex(this));
405
  ReleaseSRWLockExclusive(V8ToWindowsType(&native_handle_));
406 407 408
}

bool SharedMutex::TryLockShared() {
409
  DCHECK(SharedMutexNotHeld(this));
410
  bool result = TryAcquireSRWLockShared(V8ToWindowsType(&native_handle_));
411 412
  if (result) DCHECK(TryHoldSharedMutex(this));
  return result;
413 414 415
}

bool SharedMutex::TryLockExclusive() {
416
  DCHECK(SharedMutexNotHeld(this));
417
  bool result = TryAcquireSRWLockExclusive(V8ToWindowsType(&native_handle_));
418 419
  if (result) DCHECK(TryHoldSharedMutex(this));
  return result;
420 421
}

johnx's avatar
johnx committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
#elif V8_OS_STARBOARD

Mutex::Mutex() { SbMutexCreate(&native_handle_); }

Mutex::~Mutex() { SbMutexDestroy(&native_handle_); }

void Mutex::Lock() { SbMutexAcquire(&native_handle_); }

void Mutex::Unlock() { SbMutexRelease(&native_handle_); }

RecursiveMutex::RecursiveMutex() {}

RecursiveMutex::~RecursiveMutex() {}

void RecursiveMutex::Lock() { native_handle_.Acquire(); }

void RecursiveMutex::Unlock() { native_handle_.Release(); }

bool RecursiveMutex::TryLock() { return native_handle_.AcquireTry(); }

SharedMutex::SharedMutex() = default;

SharedMutex::~SharedMutex() = default;

446 447 448 449
void SharedMutex::LockShared() {
  DCHECK(TryHoldSharedMutex(this));
  native_handle_.AcquireReadLock();
}
johnx's avatar
johnx committed
450

451 452 453 454
void SharedMutex::LockExclusive() {
  DCHECK(TryHoldSharedMutex(this));
  native_handle_.AcquireWriteLock();
}
johnx's avatar
johnx committed
455

456 457 458 459
void SharedMutex::UnlockShared() {
  DCHECK(TryReleaseSharedMutex(this));
  native_handle_.ReleaseReadLock();
}
johnx's avatar
johnx committed
460

461 462 463 464
void SharedMutex::UnlockExclusive() {
  DCHECK(TryReleaseSharedMutex(this));
  native_handle_.ReleaseWriteLock();
}
johnx's avatar
johnx committed
465

466 467 468 469
bool SharedMutex::TryLockShared() {
  DCHECK(SharedMutexNotHeld(this));
  return false;
}
johnx's avatar
johnx committed
470

471 472 473 474
bool SharedMutex::TryLockExclusive() {
  DCHECK(SharedMutexNotHeld(this));
  return false;
}
johnx's avatar
johnx committed
475
#endif  // V8_OS_STARBOARD
476

477 478
}  // namespace base
}  // namespace v8