#pragma once #if __cplusplus < 201103L #error "C++ version lower than C++11" #endif //#if defined(RT_USING_LIBC) && defined(RT_USING_PTHREADS) #include #include #include #include #include #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 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 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 unique_lock(mutex_type& m, const chrono::time_point& abs_time) noexcept : pm(std::addressof(m)), owns(pm->try_lock_until(abs_time)) { } template unique_lock(mutex_type& m, const chrono::duration& 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 bool try_lock_for(const chrono::duration& 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 bool try_lock_until(const chrono::time_point& 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 void swap(unique_lock& x, unique_lock& y) { x.swap(y); } template int try_lock(L0& l0, L1& l1) { unique_lock 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 int try_lock(L0& l0, L1& l1, L2& l2, L3&... l3) { int r = 0; unique_lock 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 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 u0(l0); i = try_lock(l1, l2, l3...); if (i == -1) { u0.release(); return; } } ++i; sched_yield(); break; case 1: { unique_lock 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 void lock(L0& l0, L1& l1) { while (true) { { unique_lock u0(l0); if (l1.try_lock()) { u0.release(); break; } } sched_yield(); // wait and try the other way { unique_lock u1(l1); if (l0.try_lock()) { u1.release(); break; } } sched_yield(); } } template 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 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 once_functor; extern void set_once_functor_lock_ptr(unique_lock*); extern "C" void once_proxy(); // passed into pthread_once template 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 functor_lock(get_once_mutex()); auto call_wrapper = std::bind(std::forward(func), std::forward(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_LIBC) && (RT_USING_PTHREADS)