| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef FLUTTER_IMPELLER_BASE_THREAD_H_ | |
| #define FLUTTER_IMPELLER_BASE_THREAD_H_ | |
| #include <chrono> | |
| #include <condition_variable> | |
| #include <functional> | |
| #include <memory> | |
| #include <mutex> | |
| #include <thread> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/macros.h" | |
| #include "flutter/fml/synchronization/shared_mutex.h" | |
| #include "impeller/base/thread_safety.h" | |
| namespace impeller { | |
| class ConditionVariable; | |
| class IPLR_CAPABILITY("mutex") Mutex { | |
| public: | |
| Mutex() = default; | |
| ~Mutex() = default; | |
| void Lock() IPLR_ACQUIRE() { mutex_.lock(); } | |
| void Unlock() IPLR_RELEASE() { mutex_.unlock(); } | |
| private: | |
| friend class ConditionVariable; | |
| std::mutex mutex_; | |
| Mutex(const Mutex&) = delete; | |
| Mutex(Mutex&&) = delete; | |
| Mutex& operator=(const Mutex&) = delete; | |
| Mutex& operator=(Mutex&&) = delete; | |
| }; | |
| class IPLR_CAPABILITY("mutex") RWMutex { | |
| public: | |
| RWMutex() | |
| : mutex_(std::unique_ptr<fml::SharedMutex>(fml::SharedMutex::Create())) {} | |
| ~RWMutex() = default; | |
| void LockWriter() IPLR_ACQUIRE() { mutex_->Lock(); } | |
| void UnlockWriter() IPLR_RELEASE() { mutex_->Unlock(); } | |
| void LockReader() IPLR_ACQUIRE_SHARED() { mutex_->LockShared(); } | |
| void UnlockReader() IPLR_RELEASE_SHARED() { mutex_->UnlockShared(); } | |
| private: | |
| std::unique_ptr<fml::SharedMutex> mutex_; | |
| RWMutex(const RWMutex&) = delete; | |
| RWMutex(RWMutex&&) = delete; | |
| RWMutex& operator=(const RWMutex&) = delete; | |
| RWMutex& operator=(RWMutex&&) = delete; | |
| }; | |
| class IPLR_SCOPED_CAPABILITY Lock { | |
| public: | |
| explicit Lock(Mutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { | |
| mutex_.Lock(); | |
| } | |
| ~Lock() IPLR_RELEASE() { mutex_.Unlock(); } | |
| private: | |
| Mutex& mutex_; | |
| Lock(const Lock&) = delete; | |
| Lock(Lock&&) = delete; | |
| Lock& operator=(const Lock&) = delete; | |
| Lock& operator=(Lock&&) = delete; | |
| }; | |
| class IPLR_SCOPED_CAPABILITY ReaderLock { | |
| public: | |
| explicit ReaderLock(RWMutex& mutex) IPLR_ACQUIRE_SHARED(mutex) | |
| : mutex_(mutex) { | |
| mutex_.LockReader(); | |
| } | |
| ~ReaderLock() IPLR_RELEASE() { mutex_.UnlockReader(); } | |
| private: | |
| RWMutex& mutex_; | |
| ReaderLock(const ReaderLock&) = delete; | |
| ReaderLock(ReaderLock&&) = delete; | |
| ReaderLock& operator=(const ReaderLock&) = delete; | |
| ReaderLock& operator=(ReaderLock&&) = delete; | |
| }; | |
| class IPLR_SCOPED_CAPABILITY WriterLock { | |
| public: | |
| explicit WriterLock(RWMutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { | |
| mutex_.LockWriter(); | |
| } | |
| ~WriterLock() IPLR_RELEASE() { mutex_.UnlockWriter(); } | |
| private: | |
| RWMutex& mutex_; | |
| WriterLock(const WriterLock&) = delete; | |
| WriterLock(WriterLock&&) = delete; | |
| WriterLock& operator=(const WriterLock&) = delete; | |
| WriterLock& operator=(WriterLock&&) = delete; | |
| }; | |
| //------------------------------------------------------------------------------ | |
| /// @brief A condition variable exactly similar to the one in libcxx with | |
| /// two major differences: | |
| /// | |
| /// * On the Wait, WaitFor, and WaitUntil calls, static analysis | |
| /// annotation are respected. | |
| /// * There is no ability to wait on a condition variable and also | |
| /// be susceptible to spurious wakes. This is because the | |
| /// predicate is mandatory. | |
| /// | |
| class ConditionVariable { | |
| public: | |
| ConditionVariable() = default; | |
| ~ConditionVariable() = default; | |
| ConditionVariable(const ConditionVariable&) = delete; | |
| ConditionVariable& operator=(const ConditionVariable&) = delete; | |
| void NotifyOne() { cv_.notify_one(); } | |
| void NotifyAll() { cv_.notify_all(); } | |
| using Predicate = std::function<bool()>; | |
| //---------------------------------------------------------------------------- | |
| /// @brief Atomically unlocks the mutex and waits on the condition | |
| /// variable up to a specified time point. Lock will be reacquired | |
| /// when the wait exits. Spurious wakes may happen before the time | |
| /// point is reached. In such cases the predicate is invoked and | |
| /// it must return `false` for the wait to continue. The predicate | |
| /// will be invoked with the mutex locked. | |
| /// | |
| /// @note Since the predicate is invoked with the mutex locked, if it | |
| /// accesses other guarded resources, the predicate itself must be | |
| /// decorated with the IPLR_REQUIRES directive. For instance, | |
| /// | |
| /// ```c++ | |
| /// [] () IPLR_REQUIRES(mutex) { | |
| /// return my_guarded_resource.should_stop_waiting; | |
| /// } | |
| /// ``` | |
| /// | |
| /// @param mutex The mutex. | |
| /// @param[in] time_point The time point to wait to. | |
| /// @param[in] should_stop_waiting The predicate invoked on spurious wakes. | |
| /// Must return false for the wait to | |
| /// continue. | |
| /// | |
| /// @tparam Clock The clock type. | |
| /// @tparam Duration The duration type. | |
| /// | |
| /// @return The value of the predicate at the end of the wait. | |
| /// | |
| template <class Clock, class Duration> | |
| bool WaitUntil(Mutex& mutex, | |
| const std::chrono::time_point<Clock, Duration>& time_point, | |
| const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) { | |
| std::unique_lock lock(mutex.mutex_, std::adopt_lock); | |
| const auto result = cv_.wait_until(lock, time_point, should_stop_waiting); | |
| lock.release(); | |
| return result; | |
| } | |
| //---------------------------------------------------------------------------- | |
| /// @brief Atomically unlocks the mutex and waits on the condition | |
| /// variable for a designated duration. Lock will be reacquired | |
| /// when the wait exits. Spurious wakes may happen before the time | |
| /// point is reached. In such cases the predicate is invoked and | |
| /// it must return `false` for the wait to continue. The predicate | |
| /// will be invoked with the mutex locked. | |
| /// | |
| /// @note Since the predicate is invoked with the mutex locked, if it | |
| /// accesses other guarded resources, the predicate itself must be | |
| /// decorated with the IPLR_REQUIRES directive. For instance, | |
| /// | |
| /// ```c++ | |
| /// [] () IPLR_REQUIRES(mutex) { | |
| /// return my_guarded_resource.should_stop_waiting; | |
| /// } | |
| /// ``` | |
| /// | |
| /// @param mutex The mutex. | |
| /// @param[in] duration The duration to wait for. | |
| /// @param[in] should_stop_waiting The predicate invoked on spurious wakes. | |
| /// Must return false for the wait to | |
| /// continue. | |
| /// | |
| /// @tparam Representation The duration representation type. | |
| /// @tparam Period The duration period type. | |
| /// | |
| /// @return The value of the predicate at the end of the wait. | |
| /// | |
| template <class Representation, class Period> | |
| bool WaitFor(Mutex& mutex, | |
| const std::chrono::duration<Representation, Period>& duration, | |
| const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) { | |
| return WaitUntil(mutex, std::chrono::steady_clock::now() + duration, | |
| should_stop_waiting); | |
| } | |
| //---------------------------------------------------------------------------- | |
| /// @brief Atomically unlocks the mutex and waits on the condition | |
| /// variable indefinitely till the predicate determines that the | |
| /// wait must end. Lock will be reacquired when the wait exits. | |
| /// Spurious wakes may happen before the time point is reached. In | |
| /// such cases the predicate is invoked and it must return `false` | |
| /// for the wait to continue. The predicate will be invoked with | |
| /// the mutex locked. | |
| /// | |
| /// @note Since the predicate is invoked with the mutex locked, if it | |
| /// accesses other guarded resources, the predicate itself must be | |
| /// decorated with the IPLR_REQUIRES directive. For instance, | |
| /// | |
| /// ```c++ | |
| /// [] () IPLR_REQUIRES(mutex) { | |
| /// return my_guarded_resource.should_stop_waiting; | |
| /// } | |
| /// ``` | |
| /// | |
| /// @param mutex The mutex | |
| /// @param[in] should_stop_waiting The should stop waiting | |
| /// | |
| void Wait(Mutex& mutex, const Predicate& should_stop_waiting) | |
| IPLR_REQUIRES(mutex) { | |
| std::unique_lock lock(mutex.mutex_, std::adopt_lock); | |
| cv_.wait(lock, should_stop_waiting); | |
| lock.release(); | |
| } | |
| private: | |
| std::condition_variable cv_; | |
| }; | |
| } // namespace impeller | |
| #endif // FLUTTER_IMPELLER_BASE_THREAD_H_ |