rt-thread/components/cplusplus/cpp11/gcc/mutex

512 lines
15 KiB
Plaintext

#pragma once
#if __cplusplus < 201103L
#error "C++ version lower than C++11"
#endif
//#if defined(RT_USING_LIBC) && 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_LIBC) && (RT_USING_PTHREADS)