1#pragma once
2
3#if __cplusplus < 201103L
4#error "C++ version lower than C++11"
5#endif
6
7#include <pthread.h>
8
9#include <system_error>
10#include <chrono>
11#include <utility>
12#include <functional>
13#include <memory>
14
15#include "__utils.h"
16#include "mutex"
17
18#define rt_cpp_cond_var pthread_cond_t
19
20namespace std
21{
22
23    enum class cv_status
24    {
25        no_timeout,
26        timeout
27    };
28
29    class condition_variable
30    {
31    public:
32        typedef rt_cpp_cond_var *native_handle_type;
33
34        condition_variable(const condition_variable &) = delete;
35        condition_variable &operator=(const condition_variable &) = delete;
36
37        condition_variable() = default;
38
39        ~condition_variable()
40        {
41            pthread_cond_destroy(&_m_cond);
42        }
43
44        void wait(unique_lock<mutex> &lock);
45
46        void notify_one() noexcept
47        {
48            pthread_cond_signal(&_m_cond);
49        }
50
51        void notify_all() noexcept
52        {
53            pthread_cond_broadcast(&_m_cond);
54        }
55
56        template <class Predicate>
57        void wait(unique_lock<mutex> &lock, Predicate pred)
58        {
59            while (!pred())
60                wait(lock);
61        }
62
63        template <class Clock, class Duration>
64        cv_status wait_until(unique_lock<mutex> &lock,
65                             const chrono::time_point<Clock, Duration> &abs_time)
66        {
67            if (!lock.owns_lock())
68                throw_system_error((int)errc::operation_not_permitted,
69                                   "condition_variable::wailt_until: waiting on unlocked lock");
70            auto secs = chrono::time_point_cast<chrono::seconds>(abs_time);
71            auto nano_secs = chrono::duration_cast<chrono::nanoseconds>(abs_time - secs);
72
73            struct timespec c_abs_time = {static_cast<time_t>(secs.time_since_epoch().count()),
74                                          static_cast<long>(nano_secs.count())};
75
76            pthread_cond_timedwait(&_m_cond, lock.mutex()->native_handle(), &c_abs_time);
77
78            return (Clock::now() < abs_time) ? cv_status::no_timeout : cv_status::timeout;
79        }
80
81        template <class Clock, class Duration, class Predicate>
82        bool wait_until(unique_lock<mutex> &lock,
83                        const chrono::time_point<Clock, Duration> &abs_time,
84                        Predicate pred)
85        {
86            while (!pred())
87                if (wait_until(lock, abs_time) == cv_status::timeout)
88                    return pred();
89            return true;
90        }
91
92        template <class Rep, class Period>
93        cv_status wait_for(unique_lock<mutex> &lock,
94                           const chrono::duration<Rep, Period> &rel_time)
95        {
96            return wait_until(lock, real_time_clock::now() + rel_time);
97        }
98
99        template <class Rep, class Period, class Predicate>
100        bool wait_for(unique_lock<mutex> &lock,
101                      const chrono::duration<Rep, Period> &rel_time,
102                      Predicate pred)
103        {
104            return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
105        }
106
107        native_handle_type native_handle()
108        {
109            return &_m_cond;
110        }
111
112    private:
113        rt_cpp_cond_var _m_cond = PTHREAD_COND_INITIALIZER;
114    };
115
116    // Lockable is only required to have `lock()` and `unlock()`
117    class condition_variable_any
118    {
119    private:
120        condition_variable _m_cond;
121        shared_ptr<mutex> _m_mtx;
122
123        // so that Lockable automatically unlocks when waiting and locks after waiting
124        template <class Lockable>
125        struct unlocker
126        {
127            Lockable &_m_lock;
128
129            explicit unlocker(Lockable &lk)
130                : _m_lock(lk)
131            {
132                _m_lock.unlock();
133            }
134
135            ~unlocker()
136            {
137                _m_lock.lock();
138            }
139
140            unlocker(const unlocker &) = delete;
141            unlocker &operator=(const unlocker &) = delete;
142        };
143
144    public:
145        condition_variable_any() : _m_mtx(std::make_shared<mutex>()) {}
146        ~condition_variable_any() = default;
147
148        condition_variable_any(const condition_variable_any &) = delete;
149        condition_variable_any &operator=(const condition_variable_any &) = delete;
150
151        void notify_one() noexcept
152        {
153            lock_guard<mutex> lk(*_m_mtx);
154            _m_cond.notify_one();
155        }
156
157        void notify_all() noexcept
158        {
159            lock_guard<mutex> lk(*_m_mtx);
160            _m_cond.notify_all();
161        }
162
163        template <class Lock>
164        void wait(Lock &lock)
165        {
166            shared_ptr<mutex> mut = _m_mtx;
167            unique_lock<mutex> lk(*mut);
168            unlocker<Lock> auto_lk(lock); // unlock here
169
170            unique_lock<mutex> lk2(std::move(lk));
171            _m_cond.wait(lk2);
172        } // mut.unlock(); lock.lock();
173
174        template <class Lock, class Predicate>
175        void wait(Lock &lock, Predicate pred)
176        {
177            while (!pred())
178                wait(lock);
179        }
180
181        template <class Lock, class Clock, class Duration>
182        cv_status wait_until(Lock &lock,
183                             const chrono::time_point<Clock, Duration> &abs_time)
184        {
185            shared_ptr<mutex> mut = _m_mtx;
186            unique_lock<mutex> lk(*mut);
187            unlocker<Lock> auto_lk(lock); // unlock here
188
189            unique_lock<mutex> lk2(std::move(lk));
190            return _m_cond.wait_until(lk2, abs_time);
191        }
192
193        template <class Lock, class Clock, class Duration, class Predicate>
194        bool wait_until(Lock &lock,
195                        const chrono::time_point<Clock, Duration> &abs_time,
196                        Predicate pred)
197        {
198            while (!pred())
199                if (wait_until(lock, abs_time) == cv_status::timeout)
200                    return pred();
201            return true;
202        }
203
204        template <class Lock, class Rep, class Period>
205        cv_status wait_for(Lock &lock,
206                           const chrono::duration<Rep, Period> &rel_time)
207        {
208            return wait_until(lock, real_time_clock::now() + rel_time);
209        }
210
211        template <class Lock, class Rep, class Period, class Predicate>
212        bool wait_for(Lock &lock,
213                      const chrono::duration<Rep, Period> &rel_time,
214                      Predicate pred)
215        {
216            return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
217        }
218    };
219
220    void notify_all_at_thread_exit(condition_variable &cond, unique_lock<mutex> lk);
221
222} // namespace std
223