1#pragma once
2
3#if __cplusplus < 201103L
4#error "C++ version lower than C++11"
5#endif
6
7//#if defined(RT_USING_PTHREADS)
8
9#include <pthread.h>
10
11#include <system_error>
12#include <chrono>
13#include <utility>
14#include <functional>
15
16#include "__utils.h"
17
18#define rt_cpp_mutex_t  pthread_mutex_t
19
20namespace std
21{
22    // Base class on which to build std::mutex and std::timed_mutex
23    class __mutex_base
24    {
25        protected:
26            typedef rt_cpp_mutex_t  __native_type;
27
28            __native_type _m_mutex = PTHREAD_MUTEX_INITIALIZER;
29
30            constexpr __mutex_base() noexcept = default;
31            __mutex_base(const __mutex_base&) = delete;
32            __mutex_base& operator=(const __mutex_base&) = delete;
33    };
34
35
36    class mutex : private __mutex_base
37    {
38        public:
39            constexpr mutex() = default;
40            ~mutex() = default;
41
42            mutex(const mutex&) = delete;
43            mutex& operator=(const mutex&) = delete;
44
45            void lock()
46            {
47                int err  = pthread_mutex_lock(&_m_mutex);
48
49                if (err)
50                {
51                    throw_system_error(err, "mutex:lock failed.");
52                }
53            }
54
55            bool try_lock() noexcept
56            {
57                return !pthread_mutex_trylock(&_m_mutex);
58            }
59
60            void unlock() noexcept
61            {
62                pthread_mutex_unlock(&_m_mutex);
63            }
64
65            typedef __native_type* native_handle_type;
66
67            native_handle_type native_handle()
68            {
69                return &_m_mutex;
70            };
71
72    };
73
74    inline int __rt_cpp_recursive_mutex_init(rt_cpp_mutex_t* m)
75    {
76        pthread_mutexattr_t attr;
77        int res;
78
79        res = pthread_mutexattr_init(&attr);
80        if (res)
81            return res;
82        res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
83        if (res)
84            goto attr_cleanup;
85        res = pthread_mutex_init(m, &attr);
86
87        attr_cleanup:
88            int err = pthread_mutexattr_destroy(&attr);
89            return res ? res : err;
90    }
91
92    class __recursive_mutex_base
93    {
94        protected:
95            typedef rt_cpp_mutex_t __native_type;
96
97            __native_type _m_recursive_mutex;
98
99            __recursive_mutex_base(const __recursive_mutex_base&) = delete;
100            __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
101
102            __recursive_mutex_base()
103            {
104                int err = __rt_cpp_recursive_mutex_init(&_m_recursive_mutex);
105                if (err)
106                    throw_system_error(err, "Recursive mutex failed to construct");
107            }
108
109            ~__recursive_mutex_base()
110            {
111                pthread_mutex_destroy(&_m_recursive_mutex);
112            }
113    };
114
115    class recursive_mutex : private __recursive_mutex_base
116    {
117        public:
118            typedef __native_type* native_handle_type;
119            recursive_mutex() = default;
120            ~recursive_mutex() = default;
121
122            recursive_mutex(const recursive_mutex&) = delete;
123            recursive_mutex& operator=(const recursive_mutex&) = delete;
124            void lock()
125            {
126                int err = pthread_mutex_lock(&_m_recursive_mutex);
127
128                if (err)
129                    throw_system_error(err, "recursive_mutex::lock failed");
130            }
131
132            bool try_lock() noexcept
133            {
134                return !pthread_mutex_trylock(&_m_recursive_mutex);
135            }
136
137            void unlock() noexcept
138            {
139                pthread_mutex_unlock(&_m_recursive_mutex);
140            }
141
142            native_handle_type native_handle()
143            { return &_m_recursive_mutex; }
144    };
145
146#ifdef RT_PTHREAD_TIMED_MUTEX
147
148    class timed_mutex;
149
150    class recursive_timed_mutex;
151
152#endif // RT_PTHREAD_TIMED_MUTEX
153
154
155    struct defer_lock_t {};
156    struct try_to_lock_t {};
157    struct adopt_lock_t {}; // take ownership of a locked mtuex
158
159    constexpr defer_lock_t defer_lock { };
160    constexpr try_to_lock_t try_to_lock { };
161    constexpr adopt_lock_t adopt_lock { };
162
163    template <class Mutex>
164    class lock_guard
165    {
166        public:
167            typedef Mutex mutex_type;
168
169            explicit lock_guard(mutex_type& m) : pm(m) { pm.lock(); }
170            lock_guard(mutex_type& m, adopt_lock_t) noexcept : pm(m)
171            { }
172            ~lock_guard()
173            { pm.unlock(); }
174
175            lock_guard(lock_guard const&) = delete;
176            lock_guard& operator=(lock_guard const&) = delete;
177
178        private:
179            mutex_type& pm;
180
181    };
182
183    template <class Mutex>
184    class unique_lock
185    {
186        public:
187            typedef Mutex mutex_type;
188
189            unique_lock() noexcept : pm(nullptr), owns(false) { }
190
191            explicit unique_lock(mutex_type& m)
192                : pm(std::addressof(m)), owns(false)
193            {
194                lock();
195                owns = true;
196            }
197
198            unique_lock(mutex_type& m, defer_lock_t) noexcept
199                : pm(std::addressof(m)), owns(false)
200            { }
201
202            unique_lock(mutex_type& m, try_to_lock_t) noexcept
203                : pm(std::addressof(m)), owns(pm->try_lock())
204            { }
205
206            unique_lock(mutex_type& m, adopt_lock_t) noexcept
207                : pm(std::addressof(m)), owns(true)
208            { }
209
210            // any lock-involving timed mutex API is currently only for custom implementations
211            // the standard ones are not available
212            template <class Clock, class Duration>
213            unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept
214                : pm(std::addressof(m)), owns(pm->try_lock_until(abs_time))
215            { }
216
217            template <class Rep, class Period>
218            unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept
219                : pm(std::addressof(m)), owns(pm->try_lock_for(rel_time))
220            { }
221
222            ~unique_lock()
223            {
224                if (owns)
225                    unlock();
226            }
227
228            unique_lock(unique_lock const&) = delete;
229            unique_lock& operator=(unique_lock const&) = delete;
230
231            unique_lock(unique_lock&& u) noexcept
232                : pm(u.pm), owns(u.owns)
233            {
234                u.pm = nullptr;
235                u.owns = false;
236            }
237
238            unique_lock& operator=(unique_lock&& u) noexcept
239            {
240                if (owns)
241                    unlock();
242
243                unique_lock(std::move(u)).swap(*this);
244
245                u.pm = nullptr;
246                u.owns = false;
247
248                return *this;
249            }
250
251            void lock()
252            {
253                if (!pm)
254                    throw_system_error(int(errc::operation_not_permitted),
255                                        "unique_lock::lock: references null mutex");
256                else if (owns)
257                    throw_system_error(int(errc::resource_deadlock_would_occur),
258                                        "unique_lock::lock: already locked" );
259                else {
260                    pm->lock();
261                    owns = true;
262                }
263            }
264
265            bool try_lock()
266            {
267                if (!pm)
268                    throw_system_error(int(errc::operation_not_permitted),
269                                        "unique_lock::try_lock: references null mutex");
270                else if (owns)
271                    throw_system_error(int(errc::resource_deadlock_would_occur),
272                                        "unique_lock::try_lock: already locked" );
273                else {
274                    owns = pm->try_lock();
275                }
276                return owns;
277            }
278
279            template <class Rep, class Period>
280            bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
281            {
282                if (!pm)
283                    throw_system_error(int(errc::operation_not_permitted),
284                                        "unique_lock::try_lock_for: references null mutex");
285                else if (owns)
286                    throw_system_error(int(errc::resource_deadlock_would_occur),
287                                        "unique_lock::try_lock_for: already locked");
288                else {
289                    owns = pm->try_lock_for(rel_time);
290                }
291                return owns;
292            }
293
294            template <class Clock, class Duration>
295            bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
296            {
297                if (!pm)
298                    throw_system_error(int(errc::operation_not_permitted),
299                                        "unique_lock::try_lock_until: references null mutex");
300                else if (owns)
301                    throw_system_error(int(errc::resource_deadlock_would_occur),
302                                        "unique_lock::try_lock_until: already locked");
303                else {
304                    owns = pm->try_lock_until(abs_time);
305                }
306                return owns;
307            }
308
309            void unlock()
310            {
311                if (!owns)
312                    throw_system_error(int(errc::operation_not_permitted),
313                                        "unique_lock::unlock: not locked");
314                else {
315                    pm->unlock();
316                    owns = false;
317                }
318            }
319
320            void swap(unique_lock& u) noexcept
321            {
322                std::swap(pm, u.pm);
323                std::swap(owns, u.owns);
324            }
325
326            mutex_type *release() noexcept
327            {
328                mutex_type* ret_mutex = pm;
329                pm = nullptr;
330                owns = false;
331
332                return ret_mutex;
333            }
334
335            bool owns_lock() const noexcept
336            { return owns; }
337
338            explicit operator bool() const noexcept
339            { return owns_lock(); }
340
341            mutex_type* mutex() const noexcept
342            { return pm; }
343
344
345        private:
346            mutex_type *pm;
347            bool owns;
348    };
349
350    template <class Mutex>
351    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y)
352    {
353        x.swap(y);
354    }
355
356    template <class L0, class L1>
357    int try_lock(L0& l0, L1& l1)
358    {
359        unique_lock<L0> u0(l0, try_to_lock); // try to lock the first Lockable
360        // using unique_lock since we don't want to unlock l0 manually if l1 fails to lock
361        if (u0.owns_lock())
362        {
363            if (l1.try_lock()) // lock the second one
364            {
365                u0.release(); // do not let RAII of a unique_lock unlock l0
366                return -1;
367            }
368            else
369                return 1;
370        }
371        return 0;
372    }
373
374
375    template <class L0, class L1, class L2, class... L3>
376    int try_lock(L0& l0, L1& l1, L2& l2, L3&... l3)
377    {
378        int r = 0;
379        unique_lock<L0> u0(l0, try_to_lock);
380        // automatically unlock is done through RAII of unique_lock
381        if (u0.owns_lock())
382        {
383            r = try_lock(l1, l2, l3...);
384            if (r == -1)
385                u0.release();
386            else
387                ++r;
388        }
389        return r;
390    }
391
392    template <class L0, class L1, class L2, class ...L3>
393    void
394    __lock_first(int i, L0& l0, L1& l1, L2& l2, L3&... l3)
395    {
396        while (true)
397        {
398            // we first lock the one that is the most difficult to lock
399            switch (i)
400            {
401            case 0:
402                {
403                    unique_lock<L0> u0(l0);
404                    i = try_lock(l1, l2, l3...);
405                    if (i == -1)
406                    {
407                        u0.release();
408                        return;
409                    }
410                }
411                ++i;
412                sched_yield();
413                break;
414            case 1:
415                {
416                    unique_lock<L1> u1(l1);
417                    i = try_lock(l2, l3..., l0);
418                    if (i == -1)
419                    {
420                        u1.release();
421                        return;
422                    }
423                }
424                if (i == sizeof...(L3) + 1) // all except l0 are locked
425                    i = 0;
426                else
427                    i += 2; // since i was two-based above
428                sched_yield();
429                break;
430            default:
431                __lock_first(i - 2, l2, l3..., l0, l1);
432                return;
433            }
434        }
435    }
436
437
438    template <class L0, class L1>
439    void lock(L0& l0, L1& l1)
440    {
441        while (true)
442        {
443            {
444                unique_lock<L0> u0(l0);
445                if (l1.try_lock())
446                {
447                    u0.release();
448                    break;
449                }
450            }
451            sched_yield();
452            // wait and try the other way
453            {
454                unique_lock<L1> u1(l1);
455                if (l0.try_lock())
456                {
457                    u1.release();
458                    break;
459                }
460            }
461            sched_yield();
462        }
463    }
464
465    template <class L0, class L1, class... L2>
466    void lock(L0& l0, L1& l1, L2&... l2)
467    {
468        __lock_first(0, l0, l1, l2...);
469    }
470
471    struct once_flag
472    {
473        constexpr once_flag() noexcept = default;
474
475        once_flag(const once_flag&) = delete;
476        once_flag& operator=(const once_flag&) = delete;
477
478        template <class Callable, class... Args>
479        friend void call_once(once_flag& flag, Callable&& func, Args&&... args);
480
481        private:
482            pthread_once_t _m_once = PTHREAD_ONCE_INIT;
483    };
484
485    mutex& get_once_mutex();
486    extern function<void()> once_functor;
487    extern void set_once_functor_lock_ptr(unique_lock<mutex>*);
488
489    extern "C" void once_proxy(); // passed into pthread_once
490
491    template <class Callable, class... Args>
492    void call_once(once_flag& flag, Callable&& func, Args&&... args)
493    {
494        // use a lock to ensure the call to the functor
495        // is exclusive to only the first calling thread
496        unique_lock<mutex> functor_lock(get_once_mutex());
497
498        auto call_wrapper = std::bind(std::forward<Callable>(func), std::forward<Args>(args)...);
499        once_functor = [&]() { call_wrapper(); };
500
501        set_once_functor_lock_ptr(&functor_lock); // so as to unlock when actually calling
502
503        int err = pthread_once(&flag._m_once, &once_proxy);
504
505        if (functor_lock)
506            set_once_functor_lock_ptr(nullptr);
507        if (err)
508            throw_system_error(err, "call_once failed");
509    }
510}
511
512//#endif //(RT_USING_PTHREADS)