blob: c9f3fcb24548d3c2173493dd8aa767a8b49de1b8 [file] [log] [blame]
// 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_