512 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			512 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
| #pragma once
 | |
| 
 | |
| #if __cplusplus < 201103L
 | |
| #error "C++ version lower than C++11"
 | |
| #endif
 | |
| 
 | |
| //#if defined(RT_USING_PTHREADS)
 | |
| 
 | |
| #include <pthread.h>
 | |
| 
 | |
| #include <system_error>
 | |
| #include <chrono>
 | |
| #include <utility>
 | |
| #include <functional>
 | |
| 
 | |
| #include "__utils.h"
 | |
| 
 | |
| #define rt_cpp_mutex_t  pthread_mutex_t
 | |
| 
 | |
| namespace std 
 | |
| {
 | |
|     // Base class on which to build std::mutex and std::timed_mutex
 | |
|     class __mutex_base
 | |
|     {
 | |
|         protected:
 | |
|             typedef rt_cpp_mutex_t  __native_type;
 | |
| 
 | |
|             __native_type _m_mutex = PTHREAD_MUTEX_INITIALIZER;
 | |
| 
 | |
|             constexpr __mutex_base() noexcept = default;
 | |
|             __mutex_base(const __mutex_base&) = delete;
 | |
|             __mutex_base& operator=(const __mutex_base&) = delete;
 | |
|     };
 | |
| 
 | |
|     
 | |
|     class mutex : private __mutex_base
 | |
|     {
 | |
|         public:
 | |
|             constexpr mutex() = default;
 | |
|             ~mutex() = default;
 | |
| 
 | |
|             mutex(const mutex&) = delete;
 | |
|             mutex& operator=(const mutex&) = delete;
 | |
| 
 | |
|             void lock()
 | |
|             {
 | |
|                 int err  = pthread_mutex_lock(&_m_mutex);
 | |
| 
 | |
|                 if (err)
 | |
|                 {
 | |
|                     throw_system_error(err, "mutex:lock failed.");
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             bool try_lock() noexcept
 | |
|             {
 | |
|                 return !pthread_mutex_trylock(&_m_mutex);
 | |
|             }
 | |
| 
 | |
|             void unlock() noexcept
 | |
|             {
 | |
|                 pthread_mutex_unlock(&_m_mutex);
 | |
|             }
 | |
| 
 | |
|             typedef __native_type* native_handle_type;
 | |
| 
 | |
|             native_handle_type native_handle() 
 | |
|             {
 | |
|                 return &_m_mutex;
 | |
|             };
 | |
| 
 | |
|     };
 | |
| 
 | |
|     inline int __rt_cpp_recursive_mutex_init(rt_cpp_mutex_t* m)
 | |
|     {
 | |
|         pthread_mutexattr_t attr;
 | |
|         int res;
 | |
| 
 | |
|         res = pthread_mutexattr_init(&attr);
 | |
|         if (res)
 | |
|             return res;
 | |
|         res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 | |
|         if (res)
 | |
|             goto attr_cleanup;
 | |
|         res = pthread_mutex_init(m, &attr);
 | |
| 
 | |
|         attr_cleanup:
 | |
|             int err = pthread_mutexattr_destroy(&attr);
 | |
|             return res ? res : err;
 | |
|     }
 | |
| 
 | |
|     class __recursive_mutex_base
 | |
|     {
 | |
|         protected:
 | |
|             typedef rt_cpp_mutex_t __native_type;
 | |
| 
 | |
|             __native_type _m_recursive_mutex;
 | |
| 
 | |
|             __recursive_mutex_base(const __recursive_mutex_base&) = delete;
 | |
|             __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
 | |
| 
 | |
|             __recursive_mutex_base()
 | |
|             {
 | |
|                 int err = __rt_cpp_recursive_mutex_init(&_m_recursive_mutex);
 | |
|                 if (err)
 | |
|                     throw_system_error(err, "Recursive mutex failed to construct");
 | |
|             }
 | |
| 
 | |
|             ~__recursive_mutex_base()
 | |
|             {
 | |
|                 pthread_mutex_destroy(&_m_recursive_mutex);
 | |
|             }
 | |
|     };
 | |
| 
 | |
|     class recursive_mutex : private __recursive_mutex_base
 | |
|     {
 | |
|         public:
 | |
|             typedef __native_type* native_handle_type; 
 | |
|             recursive_mutex() = default;
 | |
|             ~recursive_mutex() = default;
 | |
| 
 | |
|             recursive_mutex(const recursive_mutex&) = delete;
 | |
|             recursive_mutex& operator=(const recursive_mutex&) = delete;
 | |
|             void lock()
 | |
|             {
 | |
|                 int err = pthread_mutex_lock(&_m_recursive_mutex);
 | |
| 
 | |
|                 if (err)
 | |
|                     throw_system_error(err, "recursive_mutex::lock failed");
 | |
|             }
 | |
| 
 | |
|             bool try_lock() noexcept
 | |
|             {
 | |
|                 return !pthread_mutex_trylock(&_m_recursive_mutex);
 | |
|             }
 | |
| 
 | |
|             void unlock() noexcept
 | |
|             {
 | |
|                 pthread_mutex_unlock(&_m_recursive_mutex);
 | |
|             }
 | |
| 
 | |
|             native_handle_type native_handle()
 | |
|             { return &_m_recursive_mutex; }
 | |
|     };
 | |
| 
 | |
| #ifdef RT_PTHREAD_TIMED_MUTEX
 | |
| 
 | |
|     class timed_mutex;
 | |
| 
 | |
|     class recursive_timed_mutex;
 | |
| 
 | |
| #endif // RT_PTHREAD_TIMED_MUTEX
 | |
| 
 | |
|     
 | |
|     struct defer_lock_t {};
 | |
|     struct try_to_lock_t {};
 | |
|     struct adopt_lock_t {}; // take ownership of a locked mtuex
 | |
| 
 | |
|     constexpr defer_lock_t defer_lock { };
 | |
|     constexpr try_to_lock_t try_to_lock { };
 | |
|     constexpr adopt_lock_t adopt_lock { };
 | |
| 
 | |
|     template <class Mutex> 
 | |
|     class lock_guard
 | |
|     {
 | |
|         public:
 | |
|             typedef Mutex mutex_type;
 | |
| 
 | |
|             explicit lock_guard(mutex_type& m) : pm(m) { pm.lock(); }
 | |
|             lock_guard(mutex_type& m, adopt_lock_t) noexcept : pm(m)
 | |
|             { }
 | |
|             ~lock_guard() 
 | |
|             { pm.unlock(); }
 | |
| 
 | |
|             lock_guard(lock_guard const&) = delete;
 | |
|             lock_guard& operator=(lock_guard const&) = delete;
 | |
| 
 | |
|         private:
 | |
|             mutex_type& pm;
 | |
| 
 | |
|     };
 | |
| 
 | |
|     template <class Mutex>
 | |
|     class unique_lock
 | |
|     {
 | |
|         public:
 | |
|             typedef Mutex mutex_type;
 | |
| 
 | |
|             unique_lock() noexcept : pm(nullptr), owns(false) { }
 | |
|             
 | |
|             explicit unique_lock(mutex_type& m) 
 | |
|                 : pm(std::addressof(m)), owns(false)
 | |
|             {
 | |
|                 lock();
 | |
|                 owns = true;
 | |
|             }
 | |
| 
 | |
|             unique_lock(mutex_type& m, defer_lock_t) noexcept 
 | |
|                 : pm(std::addressof(m)), owns(false)
 | |
|             { }
 | |
| 
 | |
|             unique_lock(mutex_type& m, try_to_lock_t) noexcept
 | |
|                 : pm(std::addressof(m)), owns(pm->try_lock())
 | |
|             { }
 | |
| 
 | |
|             unique_lock(mutex_type& m, adopt_lock_t) noexcept
 | |
|                 : pm(std::addressof(m)), owns(true)
 | |
|             { }
 | |
| 
 | |
|             // any lock-involving timed mutex API is currently only for custom implementations
 | |
|             // the standard ones are not available
 | |
|             template <class Clock, class Duration>
 | |
|             unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept
 | |
|                 : pm(std::addressof(m)), owns(pm->try_lock_until(abs_time))
 | |
|             { }
 | |
| 
 | |
|             template <class Rep, class Period>
 | |
|             unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept
 | |
|                 : pm(std::addressof(m)), owns(pm->try_lock_for(rel_time))
 | |
|             { }
 | |
|             
 | |
|             ~unique_lock()
 | |
|             {
 | |
|                 if (owns)
 | |
|                     unlock();
 | |
|             }
 | |
|             
 | |
|             unique_lock(unique_lock const&) = delete;
 | |
|             unique_lock& operator=(unique_lock const&) = delete;
 | |
| 
 | |
|             unique_lock(unique_lock&& u) noexcept
 | |
|                 : pm(u.pm), owns(u.owns)
 | |
|             {
 | |
|                 u.pm = nullptr;
 | |
|                 u.owns = false;
 | |
|             }
 | |
| 
 | |
|             unique_lock& operator=(unique_lock&& u) noexcept                
 | |
|             {
 | |
|                 if (owns)
 | |
|                     unlock();
 | |
|                 
 | |
|                 unique_lock(std::move(u)).swap(*this);
 | |
| 
 | |
|                 u.pm = nullptr;
 | |
|                 u.owns = false;
 | |
| 
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             void lock()
 | |
|             {
 | |
|                 if (!pm)
 | |
|                     throw_system_error(int(errc::operation_not_permitted), 
 | |
|                                         "unique_lock::lock: references null mutex");
 | |
|                 else if (owns)
 | |
|                     throw_system_error(int(errc::resource_deadlock_would_occur),
 | |
|                                         "unique_lock::lock: already locked" );
 | |
|                 else {
 | |
|                     pm->lock();
 | |
|                     owns = true;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             bool try_lock()
 | |
|             {
 | |
|                 if (!pm)
 | |
|                     throw_system_error(int(errc::operation_not_permitted), 
 | |
|                                         "unique_lock::try_lock: references null mutex");
 | |
|                 else if (owns)
 | |
|                     throw_system_error(int(errc::resource_deadlock_would_occur),
 | |
|                                         "unique_lock::try_lock: already locked" );
 | |
|                 else {
 | |
|                     owns = pm->try_lock();
 | |
|                 }
 | |
|                 return owns;
 | |
|             }
 | |
| 
 | |
|             template <class Rep, class Period>
 | |
|             bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
 | |
|             {
 | |
|                 if (!pm)
 | |
|                     throw_system_error(int(errc::operation_not_permitted),
 | |
|                                         "unique_lock::try_lock_for: references null mutex");
 | |
|                 else if (owns)
 | |
|                     throw_system_error(int(errc::resource_deadlock_would_occur),
 | |
|                                         "unique_lock::try_lock_for: already locked");
 | |
|                 else {
 | |
|                     owns = pm->try_lock_for(rel_time);
 | |
|                 }
 | |
|                 return owns;
 | |
|             }
 | |
|             
 | |
|             template <class Clock, class Duration>
 | |
|             bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
 | |
|             {
 | |
|                 if (!pm)
 | |
|                     throw_system_error(int(errc::operation_not_permitted),
 | |
|                                         "unique_lock::try_lock_until: references null mutex");
 | |
|                 else if (owns)
 | |
|                     throw_system_error(int(errc::resource_deadlock_would_occur),
 | |
|                                         "unique_lock::try_lock_until: already locked");
 | |
|                 else {
 | |
|                     owns = pm->try_lock_until(abs_time);
 | |
|                 }
 | |
|                 return owns;
 | |
|             }
 | |
|             
 | |
|             void unlock()
 | |
|             {
 | |
|                 if (!owns)
 | |
|                     throw_system_error(int(errc::operation_not_permitted), 
 | |
|                                         "unique_lock::unlock: not locked");
 | |
|                 else {
 | |
|                     pm->unlock();
 | |
|                     owns = false;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void swap(unique_lock& u) noexcept
 | |
|             {
 | |
|                 std::swap(pm, u.pm);
 | |
|                 std::swap(owns, u.owns);
 | |
|             }
 | |
| 
 | |
|             mutex_type *release() noexcept
 | |
|             {
 | |
|                 mutex_type* ret_mutex = pm;
 | |
|                 pm = nullptr;
 | |
|                 owns = false;
 | |
|                 
 | |
|                 return ret_mutex;
 | |
|             }
 | |
| 
 | |
|             bool owns_lock() const noexcept
 | |
|             { return owns; }
 | |
| 
 | |
|             explicit operator bool() const noexcept
 | |
|             { return owns_lock(); }
 | |
| 
 | |
|             mutex_type* mutex() const noexcept
 | |
|             { return pm; }
 | |
|             
 | |
|             
 | |
|         private:
 | |
|             mutex_type *pm; 
 | |
|             bool owns;
 | |
|     };
 | |
| 
 | |
|     template <class Mutex>
 | |
|     void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y)
 | |
|     {
 | |
|         x.swap(y);
 | |
|     }
 | |
| 
 | |
|     template <class L0, class L1>
 | |
|     int try_lock(L0& l0, L1& l1)
 | |
|     {
 | |
|         unique_lock<L0> u0(l0, try_to_lock); // try to lock the first Lockable
 | |
|         // using unique_lock since we don't want to unlock l0 manually if l1 fails to lock
 | |
|         if (u0.owns_lock())
 | |
|         {
 | |
|             if (l1.try_lock()) // lock the second one
 | |
|             {
 | |
|                 u0.release(); // do not let RAII of a unique_lock unlock l0
 | |
|                 return -1;
 | |
|             }
 | |
|             else
 | |
|                 return 1; 
 | |
|         }
 | |
|         return 0;
 | |
|     } 
 | |
| 
 | |
|     
 | |
|     template <class L0, class L1, class L2, class... L3>
 | |
|     int try_lock(L0& l0, L1& l1, L2& l2, L3&... l3)
 | |
|     {
 | |
|         int r = 0;
 | |
|         unique_lock<L0> u0(l0, try_to_lock);
 | |
|         // automatically unlock is done through RAII of unique_lock
 | |
|         if (u0.owns_lock())
 | |
|         {
 | |
|             r = try_lock(l1, l2, l3...);
 | |
|             if (r == -1)
 | |
|                 u0.release();
 | |
|             else
 | |
|                 ++r;
 | |
|         }
 | |
|         return r;
 | |
|     }
 | |
| 
 | |
|     template <class L0, class L1, class L2, class ...L3>
 | |
|     void
 | |
|     __lock_first(int i, L0& l0, L1& l1, L2& l2, L3&... l3)
 | |
|     {
 | |
|         while (true)
 | |
|         {
 | |
|             // we first lock the one that is the most difficult to lock 
 | |
|             switch (i) 
 | |
|             {
 | |
|             case 0:
 | |
|                 {
 | |
|                     unique_lock<L0> u0(l0);
 | |
|                     i = try_lock(l1, l2, l3...);
 | |
|                     if (i == -1)
 | |
|                     {
 | |
|                         u0.release();
 | |
|                         return;
 | |
|                     }
 | |
|                 }
 | |
|                 ++i;
 | |
|                 sched_yield();
 | |
|                 break;
 | |
|             case 1:
 | |
|                 {
 | |
|                     unique_lock<L1> u1(l1);
 | |
|                     i = try_lock(l2, l3..., l0);
 | |
|                     if (i == -1)
 | |
|                     {
 | |
|                         u1.release();
 | |
|                         return;
 | |
|                     }
 | |
|                 }
 | |
|                 if (i == sizeof...(L3) + 1) // all except l0 are locked
 | |
|                     i = 0;
 | |
|                 else
 | |
|                     i += 2; // since i was two-based above
 | |
|                 sched_yield();
 | |
|                 break;
 | |
|             default:
 | |
|                 __lock_first(i - 2, l2, l3..., l0, l1);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     template <class L0, class L1>
 | |
|     void lock(L0& l0, L1& l1)
 | |
|     {
 | |
|         while (true)
 | |
|         {
 | |
|             {
 | |
|                 unique_lock<L0> u0(l0);
 | |
|                 if (l1.try_lock())
 | |
|                 {
 | |
|                     u0.release();
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             sched_yield();
 | |
|             // wait and try the other way
 | |
|             {
 | |
|                 unique_lock<L1> u1(l1);
 | |
|                 if (l0.try_lock())
 | |
|                 {
 | |
|                     u1.release();
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             sched_yield();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     template <class L0, class L1, class... L2>
 | |
|     void lock(L0& l0, L1& l1, L2&... l2)
 | |
|     {
 | |
|         __lock_first(0, l0, l1, l2...);
 | |
|     }
 | |
| 
 | |
|     struct once_flag 
 | |
|     {
 | |
|         constexpr once_flag() noexcept = default;
 | |
| 
 | |
|         once_flag(const once_flag&) = delete;
 | |
|         once_flag& operator=(const once_flag&) = delete;
 | |
| 
 | |
|         template <class Callable, class... Args>
 | |
|         friend void call_once(once_flag& flag, Callable&& func, Args&&... args);
 | |
| 
 | |
|         private:
 | |
|             pthread_once_t _m_once = PTHREAD_ONCE_INIT;
 | |
|     };
 | |
| 
 | |
|     mutex& get_once_mutex();
 | |
|     extern function<void()> once_functor;
 | |
|     extern void set_once_functor_lock_ptr(unique_lock<mutex>*);
 | |
| 
 | |
|     extern "C" void once_proxy(); // passed into pthread_once
 | |
| 
 | |
|     template <class Callable, class... Args>
 | |
|     void call_once(once_flag& flag, Callable&& func, Args&&... args)
 | |
|     {
 | |
|         // use a lock to ensure the call to the functor
 | |
|         // is exclusive to only the first calling thread
 | |
|         unique_lock<mutex> functor_lock(get_once_mutex()); 
 | |
| 
 | |
|         auto call_wrapper = std::bind(std::forward<Callable>(func), std::forward<Args>(args)...);
 | |
|         once_functor = [&]() { call_wrapper(); };
 | |
| 
 | |
|         set_once_functor_lock_ptr(&functor_lock); // so as to unlock when actually calling
 | |
| 
 | |
|         int err = pthread_once(&flag._m_once, &once_proxy);
 | |
| 
 | |
|         if (functor_lock)
 | |
|             set_once_functor_lock_ptr(nullptr);
 | |
|         if (err)
 | |
|             throw_system_error(err, "call_once failed");
 | |
|     }
 | |
| }
 | |
| 
 | |
| //#endif //(RT_USING_PTHREADS) |