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