223 lines
6.5 KiB
Plaintext
223 lines
6.5 KiB
Plaintext
|
#pragma once
|
||
|
|
||
|
#if __cplusplus < 201103L
|
||
|
#error "C++ version lower than C++11"
|
||
|
#endif
|
||
|
|
||
|
#include <pthread.h>
|
||
|
|
||
|
#include <system_error>
|
||
|
#include <chrono>
|
||
|
#include <utility>
|
||
|
#include <functional>
|
||
|
#include <memory>
|
||
|
|
||
|
#include "__utils.h"
|
||
|
#include "mutex"
|
||
|
|
||
|
#define rt_cpp_cond_var pthread_cond_t
|
||
|
|
||
|
namespace std
|
||
|
{
|
||
|
|
||
|
enum class cv_status
|
||
|
{
|
||
|
no_timeout,
|
||
|
timeout
|
||
|
};
|
||
|
|
||
|
class condition_variable
|
||
|
{
|
||
|
public:
|
||
|
typedef rt_cpp_cond_var *native_handle_type;
|
||
|
|
||
|
condition_variable(const condition_variable &) = delete;
|
||
|
condition_variable &operator=(const condition_variable &) = delete;
|
||
|
|
||
|
condition_variable() = default;
|
||
|
|
||
|
~condition_variable()
|
||
|
{
|
||
|
pthread_cond_destroy(&_m_cond);
|
||
|
}
|
||
|
|
||
|
void wait(unique_lock<mutex> &lock);
|
||
|
|
||
|
void notify_one() noexcept
|
||
|
{
|
||
|
pthread_cond_signal(&_m_cond);
|
||
|
}
|
||
|
|
||
|
void notify_all() noexcept
|
||
|
{
|
||
|
pthread_cond_broadcast(&_m_cond);
|
||
|
}
|
||
|
|
||
|
template <class Predicate>
|
||
|
void wait(unique_lock<mutex> &lock, Predicate pred)
|
||
|
{
|
||
|
while (!pred())
|
||
|
wait(lock);
|
||
|
}
|
||
|
|
||
|
template <class Clock, class Duration>
|
||
|
cv_status wait_until(unique_lock<mutex> &lock,
|
||
|
const chrono::time_point<Clock, Duration> &abs_time)
|
||
|
{
|
||
|
if (!lock.owns_lock())
|
||
|
throw_system_error((int)errc::operation_not_permitted,
|
||
|
"condition_variable::wailt_until: waiting on unlocked lock");
|
||
|
auto secs = chrono::time_point_cast<chrono::seconds>(abs_time);
|
||
|
auto nano_secs = chrono::duration_cast<chrono::nanoseconds>(abs_time - secs);
|
||
|
|
||
|
struct timespec c_abs_time = {static_cast<time_t>(secs.time_since_epoch().count()),
|
||
|
static_cast<long>(nano_secs.count())};
|
||
|
|
||
|
pthread_cond_timedwait(&_m_cond, lock.mutex()->native_handle(), &c_abs_time);
|
||
|
|
||
|
return (Clock::now() < abs_time) ? cv_status::no_timeout : cv_status::timeout;
|
||
|
}
|
||
|
|
||
|
template <class Clock, class Duration, class Predicate>
|
||
|
bool wait_until(unique_lock<mutex> &lock,
|
||
|
const chrono::time_point<Clock, Duration> &abs_time,
|
||
|
Predicate pred)
|
||
|
{
|
||
|
while (!pred())
|
||
|
if (wait_until(lock, abs_time) == cv_status::timeout)
|
||
|
return pred();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <class Rep, class Period>
|
||
|
cv_status wait_for(unique_lock<mutex> &lock,
|
||
|
const chrono::duration<Rep, Period> &rel_time)
|
||
|
{
|
||
|
return wait_until(lock, real_time_clock::now() + rel_time);
|
||
|
}
|
||
|
|
||
|
template <class Rep, class Period, class Predicate>
|
||
|
bool wait_for(unique_lock<mutex> &lock,
|
||
|
const chrono::duration<Rep, Period> &rel_time,
|
||
|
Predicate pred)
|
||
|
{
|
||
|
return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
|
||
|
}
|
||
|
|
||
|
native_handle_type native_handle()
|
||
|
{
|
||
|
return &_m_cond;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
rt_cpp_cond_var _m_cond = PTHREAD_COND_INITIALIZER;
|
||
|
};
|
||
|
|
||
|
// Lockable is only required to have `lock()` and `unlock()`
|
||
|
class condition_variable_any
|
||
|
{
|
||
|
private:
|
||
|
condition_variable _m_cond;
|
||
|
shared_ptr<mutex> _m_mtx;
|
||
|
|
||
|
// so that Lockable automatically unlocks when waiting and locks after waiting
|
||
|
template <class Lockable>
|
||
|
struct unlocker
|
||
|
{
|
||
|
Lockable &_m_lock;
|
||
|
|
||
|
explicit unlocker(Lockable &lk)
|
||
|
: _m_lock(lk)
|
||
|
{
|
||
|
_m_lock.unlock();
|
||
|
}
|
||
|
|
||
|
~unlocker()
|
||
|
{
|
||
|
_m_lock.lock();
|
||
|
}
|
||
|
|
||
|
unlocker(const unlocker &) = delete;
|
||
|
unlocker &operator=(const unlocker &) = delete;
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
condition_variable_any() : _m_mtx(std::make_shared<mutex>()) {}
|
||
|
~condition_variable_any() = default;
|
||
|
|
||
|
condition_variable_any(const condition_variable_any &) = delete;
|
||
|
condition_variable_any &operator=(const condition_variable_any &) = delete;
|
||
|
|
||
|
void notify_one() noexcept
|
||
|
{
|
||
|
lock_guard<mutex> lk(*_m_mtx);
|
||
|
_m_cond.notify_one();
|
||
|
}
|
||
|
|
||
|
void notify_all() noexcept
|
||
|
{
|
||
|
lock_guard<mutex> lk(*_m_mtx);
|
||
|
_m_cond.notify_all();
|
||
|
}
|
||
|
|
||
|
template <class Lock>
|
||
|
void wait(Lock &lock)
|
||
|
{
|
||
|
shared_ptr<mutex> mut = _m_mtx;
|
||
|
unique_lock<mutex> lk(*mut);
|
||
|
unlocker<Lock> auto_lk(lock); // unlock here
|
||
|
|
||
|
unique_lock<mutex> lk2(std::move(lk));
|
||
|
_m_cond.wait(lk2);
|
||
|
} // mut.unlock(); lock.lock();
|
||
|
|
||
|
template <class Lock, class Predicate>
|
||
|
void wait(Lock &lock, Predicate pred)
|
||
|
{
|
||
|
while (!pred())
|
||
|
wait(lock);
|
||
|
}
|
||
|
|
||
|
template <class Lock, class Clock, class Duration>
|
||
|
cv_status wait_until(Lock &lock,
|
||
|
const chrono::time_point<Clock, Duration> &abs_time)
|
||
|
{
|
||
|
shared_ptr<mutex> mut = _m_mtx;
|
||
|
unique_lock<mutex> lk(*mut);
|
||
|
unlocker<Lock> auto_lk(lock); // unlock here
|
||
|
|
||
|
unique_lock<mutex> lk2(std::move(lk));
|
||
|
return _m_cond.wait_until(lk2, abs_time);
|
||
|
}
|
||
|
|
||
|
template <class Lock, class Clock, class Duration, class Predicate>
|
||
|
bool wait_until(Lock &lock,
|
||
|
const chrono::time_point<Clock, Duration> &abs_time,
|
||
|
Predicate pred)
|
||
|
{
|
||
|
while (!pred())
|
||
|
if (wait_until(lock, abs_time) == cv_status::timeout)
|
||
|
return pred();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <class Lock, class Rep, class Period>
|
||
|
cv_status wait_for(Lock &lock,
|
||
|
const chrono::duration<Rep, Period> &rel_time)
|
||
|
{
|
||
|
return wait_until(lock, real_time_clock::now() + rel_time);
|
||
|
}
|
||
|
|
||
|
template <class Lock, class Rep, class Period, class Predicate>
|
||
|
bool wait_for(Lock &lock,
|
||
|
const chrono::duration<Rep, Period> &rel_time,
|
||
|
Predicate pred)
|
||
|
{
|
||
|
return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void notify_all_at_thread_exit(condition_variable &cond, unique_lock<mutex> lk);
|
||
|
|
||
|
} // namespace std
|