74846b96c3
Existing Monitor is not reentrable, doesn't support acquiring it if it is already acquired by this thread. TEST=ReentrantMonitorAllowsReentrance Change-Id: Id74479388a99ca907bca6e379c3a7e08191e841b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436542 Commit-Queue: Alexander Aprelev <aam@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
222 lines
5.4 KiB
C++
222 lines
5.4 KiB
C++
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_PLATFORM_SYNCHRONIZATION_H_
|
|
#define RUNTIME_PLATFORM_SYNCHRONIZATION_H_
|
|
|
|
#include "platform/allocation.h"
|
|
#include "platform/threads.h"
|
|
|
|
#if defined(DART_USE_ABSL)
|
|
#include "third_party/absl/synchronization/mutex.h"
|
|
#endif
|
|
|
|
namespace dart {
|
|
|
|
#if defined(DART_USE_ABSL)
|
|
using MutexImpl = absl::Mutex;
|
|
using ConditionVariableImpl = absl::CondVar;
|
|
#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_FUCHSIA) || \
|
|
defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_ANDROID)
|
|
using MutexImpl = pthread_mutex_t;
|
|
using ConditionVariableImpl = pthread_cond_t;
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
using MutexImpl = SRWLOCK;
|
|
using ConditionVariableImpl = CONDITION_VARIABLE;
|
|
#else
|
|
#error Unknown target os.
|
|
#endif
|
|
|
|
// Mark when we are running in a signal handler (Linux, Android) or with a
|
|
// suspended thread (Windows, Mac, Fuchia). During this time, we cannot take
|
|
// locks.
|
|
class DisallowMutexLockingScope : public ValueObject {
|
|
#if defined(DEBUG)
|
|
public:
|
|
DisallowMutexLockingScope() {
|
|
ASSERT(!is_active_);
|
|
is_active_ = true;
|
|
}
|
|
|
|
~DisallowMutexLockingScope() { is_active_ = false; }
|
|
|
|
static bool is_active() { return is_active_; }
|
|
|
|
private:
|
|
static inline thread_local bool is_active_ = false;
|
|
#endif // DEBUG
|
|
};
|
|
|
|
class Mutex {
|
|
public:
|
|
Mutex();
|
|
~Mutex();
|
|
|
|
bool IsOwnedByCurrentThread() const {
|
|
return owner_.IsOwnedByCurrentThread();
|
|
}
|
|
|
|
void Lock();
|
|
bool TryLock(); // Returns false if lock is busy and locking failed.
|
|
void Unlock();
|
|
|
|
private:
|
|
MutexImpl mutex_;
|
|
platform::ThreadBoundResource owner_;
|
|
|
|
friend class ConditionVariable;
|
|
DISALLOW_COPY_AND_ASSIGN(Mutex);
|
|
};
|
|
|
|
class ConditionVariable {
|
|
public:
|
|
enum WaitResult { kNotified, kTimedOut };
|
|
static constexpr int64_t kNoTimeout = 0;
|
|
|
|
ConditionVariable();
|
|
~ConditionVariable();
|
|
|
|
WaitResult Wait(Mutex* mutex, int64_t timeout_millis = kNoTimeout);
|
|
|
|
WaitResult WaitMicros(Mutex* mutex, int64_t timeout_micros = kNoTimeout);
|
|
|
|
void Notify();
|
|
void NotifyAll();
|
|
|
|
private:
|
|
ConditionVariableImpl cv_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
|
|
};
|
|
|
|
class Monitor {
|
|
public:
|
|
using WaitResult = ConditionVariable::WaitResult;
|
|
static constexpr WaitResult kNotified = ConditionVariable::kNotified;
|
|
static constexpr WaitResult kTimedOut = ConditionVariable::kTimedOut;
|
|
|
|
static constexpr int64_t kNoTimeout = ConditionVariable::kNoTimeout;
|
|
|
|
Monitor() {}
|
|
~Monitor() {}
|
|
|
|
bool IsOwnedByCurrentThread() const {
|
|
return mutex_.IsOwnedByCurrentThread();
|
|
}
|
|
|
|
bool TryEnter() { return mutex_.TryLock(); }
|
|
void Enter() { return mutex_.Lock(); }
|
|
void Exit() { return mutex_.Unlock(); }
|
|
|
|
// Wait for notification or timeout.
|
|
WaitResult Wait(int64_t timeout_millis) {
|
|
return cv_.Wait(&mutex_, timeout_millis);
|
|
}
|
|
|
|
WaitResult WaitMicros(int64_t timeout_micros) {
|
|
return cv_.WaitMicros(&mutex_, timeout_micros);
|
|
}
|
|
|
|
// Notify waiting threads.
|
|
void Notify() { cv_.Notify(); }
|
|
|
|
void NotifyAll() { cv_.NotifyAll(); }
|
|
|
|
private:
|
|
Mutex mutex_; // OS-specific data.
|
|
ConditionVariable cv_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Monitor);
|
|
};
|
|
|
|
class ReentrantMonitor {
|
|
public:
|
|
using WaitResult = ConditionVariable::WaitResult;
|
|
static constexpr WaitResult kNotified = ConditionVariable::kNotified;
|
|
static constexpr WaitResult kTimedOut = ConditionVariable::kTimedOut;
|
|
|
|
static constexpr int64_t kNoTimeout = ConditionVariable::kNoTimeout;
|
|
|
|
ReentrantMonitor() {}
|
|
~ReentrantMonitor() {}
|
|
|
|
bool IsOwnedByCurrentThread() const {
|
|
return owner_ == platform::GetCurrentThreadId();
|
|
}
|
|
|
|
bool TryEnter() {
|
|
bool entered = false;
|
|
if (IsOwnedByCurrentThread()) {
|
|
entered = true;
|
|
} else {
|
|
entered = mutex_.TryLock();
|
|
if (entered) {
|
|
owner_ = platform::GetCurrentThreadId();
|
|
}
|
|
}
|
|
if (entered) {
|
|
++depth_;
|
|
ASSERT(owner_ == platform::GetCurrentThreadId());
|
|
}
|
|
return entered;
|
|
}
|
|
|
|
void Enter() {
|
|
ASSERT(depth_ >= 0);
|
|
if (!IsOwnedByCurrentThread()) {
|
|
mutex_.Lock();
|
|
owner_ = platform::GetCurrentThreadId();
|
|
}
|
|
ASSERT(owner_ == platform::GetCurrentThreadId());
|
|
++depth_;
|
|
}
|
|
|
|
void Exit() {
|
|
ASSERT(depth_ > 0);
|
|
ASSERT(owner_ == platform::GetCurrentThreadId());
|
|
if (--depth_ == 0) {
|
|
owner_ = platform::kInvalidThreadId;
|
|
mutex_.Unlock();
|
|
}
|
|
}
|
|
|
|
WaitResult Wait(int64_t timeout_millis) {
|
|
ASSERT(IsOwnedByCurrentThread() && depth_ == 1);
|
|
--depth_;
|
|
owner_ = platform::kInvalidThreadId;
|
|
WaitResult result = cv_.Wait(&mutex_, timeout_millis);
|
|
owner_ = platform::GetCurrentThreadId();
|
|
++depth_;
|
|
return result;
|
|
}
|
|
|
|
WaitResult WaitMicros(int64_t timeout_micros) {
|
|
ASSERT(IsOwnedByCurrentThread() && depth_ == 1);
|
|
--depth_;
|
|
owner_ = platform::kInvalidThreadId;
|
|
WaitResult result = cv_.WaitMicros(&mutex_, timeout_micros);
|
|
owner_ = platform::GetCurrentThreadId();
|
|
++depth_;
|
|
return result;
|
|
}
|
|
|
|
// Notify waiting threads.
|
|
void Notify() { cv_.Notify(); }
|
|
|
|
void NotifyAll() { cv_.NotifyAll(); }
|
|
|
|
private:
|
|
Mutex mutex_; // OS-specific data.
|
|
ConditionVariable cv_;
|
|
|
|
intptr_t depth_ = 0;
|
|
platform::ThreadId owner_ = platform::kInvalidThreadId;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ReentrantMonitor);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_PLATFORM_SYNCHRONIZATION_H_
|