| // 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_ |