从 C++11 开始,许多多线程编程组件被引入了标准库。本文分析 C++ 标准库提供的 6 种互斥锁。
C++ 多线程详解系列文章
本文内容
2、互斥锁 mutex
当多个线程同时修改共享变量时,会出现不可预知的 BUG。多线程环境需要对共享变量添加互斥锁强制有序访问,避免出现竞争情况,互斥锁可以保护共享数据不被多个线程同时修改。
C++ 提供多种互斥锁实现,以供不同场景的使用。
2.1、std::mutex (C++11)
std::mutex 是普通的互斥锁,提供三个简单的接口:lock/try_lock/unlock。try_lock() 如果成功获取锁,返回 true,否则返回 flase,不会阻塞地等待。
#include
#include
#include
int g_num = 0; // protected by g_num_mutex
std::mutex g_num_mutex;
void slow_increment(int id) {
for (int i = 0; i < 3; ++i) {
g_num_mutex.lock();
++g_num;
std::cout << "id: " << id << ", g_num: " << g_num << 'n';
g_num_mutex.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(234));
}
}
int main() {
std::thread t1{slow_increment, 0};
std::thread t2{slow_increment, 1};
t1.join();
t2.join();
return 0;
}
输出为
id: 0, g_num: 1
id: 1, g_num: 2
id: 1, g_num: 3
id: 0, g_num: 4
id: 0, g_num: 5
id: 1, g_num: 6
std::mutex 不支持拷贝和移动,并且不支持递归使用。在 Linux 系统下,是对系统 pthread_mutex_t 简单的封装。
/// std_mutex.h
class __mutex_base
{
protected:
typedef __gthread_mutex_t __native_type;
#ifdef __GTHREAD_MUTEX_INIT
__native_type _M_mutex = __GTHREAD_MUTEX_INIT;
constexpr __mutex_base() noexcept = default;
#else
// ...
#endif
__mutex_base(const __mutex_base&) = delete;
__mutex_base& operator=(const __mutex_base&) = delete;
};
/// The standard mutex type.
class mutex : private __mutex_base
{
public:
typedef __native_type* native_handle_type;
#ifdef __GTHREAD_MUTEX_INIT
constexpr
#endif
mutex() noexcept = default;
~mutex() = default;
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_mutex_trylock(&_M_mutex);
}
void
unlock()
{
// XXX EINVAL, EAGAIN, EPERM
__gthread_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
};
__gthread_mutex_lock/try_lock/unlock 直接调用 pthread_mutex_lock/try_lock/unlock,没有其他特殊的操作。
/// gthr-default.h
#include
typedef pthread_mutex_t __gthread_mutex_t;
typedef pthread_mutex_t __gthread_recursive_mutex_t;
#define __GTHREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
static inline int
__gthread_mutex_lock (__gthread_mutex_t *__mutex)
{
if (__gthread_active_p ())
return __gthrw_(pthread_mutex_lock) (__mutex);
else
return 0;
}
static inline int
__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
{
if (__gthread_active_p ())
return __gthrw_(pthread_mutex_trylock) (__mutex);
else
return 0;
}
static inline int
__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
{
if (__gthread_active_p ())
return __gthrw_(pthread_mutex_unlock) (__mutex);
else
return 0;
}
2.2、std::timed_mutex (c++11)
std::timed_mutex 虽然实现上和 std::mutex 没有关系,但是功能上是对 std::mutex 的拓展,新增 try_lock_for/try_lock_until 两个接口,支持在 lock 时设置 timeout 参数。和 std::mutex 一样,std::timed_mutex 不支持递归使用。
#include
#include
#include
#include
#include
#include
std::mutex cout_mutex; // control access to std::cout
std::timed_mutex mutex;
const std::chrono::milliseconds k100Ms = std::chrono::milliseconds(100);
void job(int id) {
std::ostringstream stream;
for (int i = 0; i < 3; ++i) {
if (mutex.try_lock_for(k100Ms)) {
stream << "success ";
std::this_thread::sleep_for(k100Ms);
mutex.unlock();
} else {
stream << "failed ";
}
std::this_thread::sleep_for(k100Ms);
}
cout_mutex.lock();
std::cout << "[" << id << "] " << stream.str() << "n";
cout_mutex.unlock();
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back(job, i);
}
for (auto& i : threads) {
i.join();
}
}
timed_mutex 继承于 __mutex_base 和 __timed_mutex_impl。__mutex_base 前文有介绍,Linux 平台下封装了 pthread_mutex_t 变量;__timed_mutex_impl 实现了 _M_try_lock_for() 和 _M_try_lock_until() 两个函数。
class timed_mutex
: private __mutex_base, public __timed_mutex_impl<timed_mutex>
{
public:
typedef __native_type* native_handle_type;
timed_mutex() = default;
~timed_mutex() = default;
timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_mutex_trylock(&_M_mutex);
}
template <class _Rep, class _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{ return _M_try_lock_for(__rtime); } // __timed_mutex_impl 接口
template <class _Clock, class _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{ return _M_try_lock_until(__atime); } // __timed_mutex_impl 接口
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_mutex_unlock(&_M_mutex); // pthread_mutex_t 接口函数
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
private:
friend class __timed_mutex_impl<timed_mutex>;
bool
_M_timedlock(const __gthread_time_t& __ts)
{ return !__gthread_mutex_timedlock(&_M_mutex, &__ts); }
#if _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK
bool
_M_clocklock(clockid_t clockid, const __gthread_time_t& __ts)
{ return !pthread_mutex_clocklock(&_M_mutex, clockid, &__ts); }
#endif
};
__timed_mutex_impl 调用的是 _Derived 的接口:将 this 强制转换为 _Derived* 类型。从实现上看,timed_mutex 继承 __timed_mutex_impl 时传入的模板参数是 timed_mutex,所以 __timed_mutex_impl 最后会调用 timed_mutex 的实现。
/// mutex
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
template<typename _Derived>
class __timed_mutex_impl
{
protected:
template<typename _Rep, typename _Period>
bool
_M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{
#if _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK
using __clock = chrono::steady_clock;
#else
using __clock = chrono::system_clock;
#endif
auto __rt = chrono::duration_cast<__clock::duration>(__rtime);
if (ratio_greater<__clock::period, _Period>())
++__rt;
return _M_try_lock_until(__clock::now() + __rt);
}
template<typename _Duration>
bool
_M_try_lock_until(const chrono::time_point<chrono::system_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts = {
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
// std::timed_mutex::_M_timedlock() 函数
return static_cast<_Derived*>(this)->_M_timedlock(__ts);
}
#ifdef _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK
template<typename _Duration>
bool
_M_try_lock_until(const chrono::time_point<chrono::steady_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts = {
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
// std::timed_mutex::_M_clocklock() 函数
return static_cast<_Derived*>(this)->_M_clocklock(CLOCK_MONOTONIC,
__ts);
}
#endif
template<typename _Clock, typename _Duration>
bool
_M_try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{
#if __cplusplus > 201703L
static_assert(chrono::is_clock_v<_Clock>);
#endif
// The user-supplied clock may not tick at the same rate as
// steady_clock, so we must loop in order to guarantee that
// the timeout has expired before returning false.
auto __now = _Clock::now();
do {
auto __rtime = __atime - __now;
if (_M_try_lock_for(__rtime))
return true;
__now = _Clock::now();
} while (__atime > __now);
return false;
}
};
2.3、std::recursive_mutex (C++11)
std::recursive_mutex 接口和 std::mutex 完全一致,但是调用的系统函数完全不同,因为 std::recursive_mutex支持递归调用:当调用 lock/try_lock 获取锁后,可以继续调用 lock/try_lock 而不会导致死锁。
#include
#include
#include
class Foo {
std::recursive_mutex m;
std::string shared;
public:
void Bar1() {
m.lock();
shared = "Bar1";
std::cout << "in Bar1, shared variable is now " << shared << 'n';
m.unlock();
}
void Bar2() {
m.lock();
shared = "Bar2";
std::cout << "in Bar2, shared variable is now " << shared << 'n';
Bar1(); // recursive lock becomes useful here
std::cout << "back in Bar2, shared variable is " << shared << 'n';
m.unlock();
};
};
int main() {
Foo x;
std::thread t1(&Foo::Bar1, &x);
std::thread t2(&Foo::Bar2, &x);
t1.join();
t2.join();
}
输出为
in Bar1, shared variable is now Bar1
in Bar2, shared variable is now Bar2
in Bar1, shared variable is now Bar1
back in Bar2, shared variable is Bar1
std::recursive_mutex 的实现和 std::mutex 类似,首先 __recursive_mutex_base 封装一个 pthread_mutex_t 数据结构(Linux 平台)
/// mutex
class __recursive_mutex_base
{
protected:
typedef __gthread_recursive_mutex_t __native_type; // pthread_mutex_t
__recursive_mutex_base(const __recursive_mutex_base&) = delete;
__recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
__native_type _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;
__recursive_mutex_base() = default;
#else
__native_type _M_mutex;
__recursive_mutex_base()
{
// XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
}
~__recursive_mutex_base()
{ __gthread_recursive_mutex_destroy(&_M_mutex); }
#endif
};
然后 recursive_mutex 继承 __recursive_mutex_base 类,实现 lock/unlock 接口。
/// 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 __e = __gthread_recursive_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_recursive_mutex_trylock(&_M_mutex);
}
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_recursive_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
};
使用 recursive_mutex 的地方,可以进行拆解,提供 *_unlock 版本的函数,进而使用 mutex。
2.4、std::recursive_timed_mutex(C++11)
std::recursive_timed_mutex 和 std::timed_mutex 实现原理相同,接口也相同:try_lock_for/try_lock_until() 函数可以指定超时时间,不用死等。和 std::timed_mutex 不同的是 std::recursive_timed_mutex 支持递归调用。
/// mutex
class recursive_timed_mutex
: private __recursive_mutex_base,
public __timed_mutex_impl<recursive_timed_mutex>
{
public:
typedef __native_type* native_handle_type;
recursive_timed_mutex() = default;
~recursive_timed_mutex() = default;
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
void
lock()
{
int __e = __gthread_recursive_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_recursive_mutex_trylock(&_M_mutex);
}
template <class _Rep, class _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{ return _M_try_lock_for(__rtime); }
template <class _Clock, class _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{ return _M_try_lock_until(__atime); }
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_recursive_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle() noexcept
{ return &_M_mutex; }
private:
friend class __timed_mutex_impl<recursive_timed_mutex>;
bool
_M_timedlock(const __gthread_time_t& __ts)
{ return !__gthread_recursive_mutex_timedlock(&_M_mutex, &__ts); }
#ifdef _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK
bool
_M_clocklock(clockid_t clockid, const __gthread_time_t& __ts)
{ return !pthread_mutex_clocklock(&_M_mutex, clockid, &__ts); }
#endif
};
2.5、std::shared_mutex (C++17)
C++17 提供 std::shared_mutex, 具有两种接口:
如果一个线程通过 lock/try_lock 获取到了锁,其他线程就不能获取到锁,包括 shared,通常用在读多写少的场景。
#include
#include
#include
#include
#include
class ThreadSafeCounter {
public:
ThreadSafeCounter() = default;
// Multiple threads/readers can read the counter's value at the same time.
unsigned int get() const {
int ret = 0;
mutex_.lock_shared();
ret = value_;
mutex_.unlock_shared();
return ret;
}
// Only one thread/writer can increment/write the counter's value.
void increment() {
mutex_.lock();
++value_;
mutex_.unlock();
}
// Only one thread/writer can reset/write the counter's value.
void reset() {
mutex_.lock();
value_ = 0;
mutex_.unlock();
}
private:
mutable std::shared_mutex mutex_;
unsigned int value_{};
};
int main() {
ThreadSafeCounter counter;
auto increment_and_print = [&counter]() {
for (int i{}; i != 3; ++i) {
counter.increment();
std::ostringstream oss;
oss << std::this_thread::get_id() << ' ' << counter.get() << 'n';
std::cout << oss.str();
}
};
std::thread thread1(increment_and_print);
std::thread thread2(increment_and_print);
thread1.join();
thread2.join();
}
在 Linux 平台,默认情况下 (_GLIBCXX_USE_PTHREAD_RWLOCK_T 定义),借助 __shared_mutex_pthread 实现,否则是借助 __shared_mutex_cv 实现。
/// shared_mutex
#if __cplusplus >= 201703L
/// The standard shared mutex type.
class shared_mutex
{
public:
shared_mutex() = default;
~shared_mutex() = default;
shared_mutex(const shared_mutex&) = delete;
shared_mutex& operator=(const shared_mutex&) = delete;
// Exclusive ownership
void lock() { _M_impl.lock(); }
bool try_lock() { return _M_impl.try_lock(); }
void unlock() { _M_impl.unlock(); }
// Shared ownership
void lock_shared() { _M_impl.lock_shared(); }
bool try_lock_shared() { return _M_impl.try_lock_shared(); }
void unlock_shared() { _M_impl.unlock_shared(); }
#if _GLIBCXX_USE_PTHREAD_RWLOCK_T
typedef void* native_handle_type;
native_handle_type native_handle() { return _M_impl.native_handle(); }
private:
__shared_mutex_pthread _M_impl;
#else
private:
__shared_mutex_cv _M_impl;
#endif
};
#endif // C++17
__shared_mutex_pthread 其实就是封装了读写锁 pthread_rwlock_t。
/// shared_mutex
/// A shared mutex type implemented using pthread_rwlock_t.
class __shared_mutex_pthread
{
friend class shared_timed_mutex;
#ifdef PTHREAD_RWLOCK_INITIALIZER
pthread_rwlock_t _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;
public:
__shared_mutex_pthread() = default;
~__shared_mutex_pthread() = default;
#else
// ...
#endif
__shared_mutex_pthread(const __shared_mutex_pthread&) = delete;
__shared_mutex_pthread& operator=(const __shared_mutex_pthread&) = delete;
void
lock()
{
int __ret = __glibcxx_rwlock_wrlock(&_M_rwlock);
if (__ret == EDEADLK)
__throw_system_error(int(errc::resource_deadlock_would_occur));
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
}
bool
try_lock()
{
int __ret = __glibcxx_rwlock_trywrlock(&_M_rwlock);
if (__ret == EBUSY) return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
void
unlock()
{
int __ret __attribute((__unused__)) = __glibcxx_rwlock_unlock(&_M_rwlock);
// Errors not handled: EPERM, EBUSY, EINVAL
__glibcxx_assert(__ret == 0);
}
// Shared ownership
void
lock_shared()
{
int __ret;
// We retry if we exceeded the maximum number of read locks supported by
// the POSIX implementation; this can result in busy-waiting, but this
// is okay based on the current specification of forward progress
// guarantees by the standard.
do
__ret = __glibcxx_rwlock_rdlock(&_M_rwlock);
while (__ret == EAGAIN);
if (__ret == EDEADLK)
__throw_system_error(int(errc::resource_deadlock_would_occur));
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
}
bool
try_lock_shared()
{
int __ret = __glibcxx_rwlock_tryrdlock(&_M_rwlock);
// If the maximum number of read locks has been exceeded, we just fail
// to acquire the lock. Unlike for lock(), we are not allowed to throw
// an exception.
if (__ret == EBUSY || __ret == EAGAIN) return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
void
unlock_shared()
{
unlock();
}
void* native_handle() { return &_M_rwlock; }
};
如果不使用 pthread_rwlock_t,C++ 使用的是 __shared_mutex_cv,用 std::mutex 和两个 std::condition_variable 实现。
如果有自己实现读写锁的需求,可以参考 GCC 下面的设计
/// shared_mutex
class __shared_mutex_cv
{
friend class shared_timed_mutex;
// Only locked when accessing _M_state or waiting on condition variables.
mutex _M_mut;
// Used to block while write-entered is set or reader count at maximum.
condition_variable _M_gate1;
// Used to block queued writers while reader count is non-zero.
condition_variable _M_gate2;
// The write-entered flag and reader count.
unsigned _M_state;
static constexpr unsigned _S_write_entered
= 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
static constexpr unsigned _S_max_readers = ~_S_write_entered;
// 是否存在 writer
bool _M_write_entered() const { return _M_state & _S_write_entered; }
// reader 的个数
unsigned _M_readers() const { return _M_state & _S_max_readers; }
public:
__shared_mutex_cv() : _M_state(0) {}
~__shared_mutex_cv()
{
__glibcxx_assert( _M_state == 0 );
}
__shared_mutex_cv(const __shared_mutex_cv&) = delete;
__shared_mutex_cv& operator=(const __shared_mutex_cv&) = delete;
// Exclusive ownership
void
lock()
{
unique_lock<mutex> __lk(_M_mut);
// 等待直到没有任何 writer
_M_gate1.wait(__lk, [=]{ return !_M_write_entered(); });
_M_state |= _S_write_entered;
// 等待直到没有任何 reader
_M_gate2.wait(__lk, [=]{ return _M_readers() == 0; });
}
bool
try_lock()
{
unique_lock<mutex> __lk(_M_mut, try_to_lock);
if (__lk.owns_lock() && _M_state == 0)
{
_M_state = _S_write_entered;
return true;
}
return false;
}
void
unlock()
{
lock_guard<mutex> __lk(_M_mut);
__glibcxx_assert( _M_write_entered() );
_M_state = 0;
// call notify_all() while mutex is held so that another thread can't
// lock and unlock the mutex then destroy *this before we make the call.
_M_gate1.notify_all();
}
// Shared ownership
void
lock_shared()
{
unique_lock<mutex> __lk(_M_mut);
_M_gate1.wait(__lk, [=]{ return _M_state < _S_max_readers; });
++_M_state;
}
bool
try_lock_shared()
{
unique_lock<mutex> __lk(_M_mut, try_to_lock);
if (!__lk.owns_lock())
return false;
if (_M_state < _S_max_readers)
{
++_M_state;
return true;
}
return false;
}
void
unlock_shared()
{
lock_guard<mutex> __lk(_M_mut);
__glibcxx_assert( _M_readers() > 0 );
auto __prev = _M_state--;
if (_M_write_entered())
{
// Wake the queued writer if there are no more readers.
if (_M_readers() == 0)
_M_gate2.notify_one();
// No need to notify gate1 because we give priority to the queued
// writer, and that writer will eventually notify gate1 after it
// clears the write-entered flag.
}
else
{
// Wake any thread that was blocked on reader overflow.
if (__prev == _S_max_readers)
_M_gate1.notify_one();
}
}
};
2.6、std::shared_timed_mutex (C++14)
std::shared_timed_mutex 和 std::timed_mutex 相同,lock 时可以设置超时时间。
/// The standard shared timed mutex type.
class shared_timed_mutex
: private __shared_timed_mutex_base
{
using _Base = __shared_timed_mutex_base;
// Must use the same clock as condition_variable for __shared_mutex_cv.
#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
using __clock_t = chrono::steady_clock;
#else
using __clock_t = chrono::system_clock;
#endif
public:
shared_timed_mutex() = default;
~shared_timed_mutex() = default;
shared_timed_mutex(const shared_timed_mutex&) = delete;
shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
// Exclusive ownership
void lock() { _Base::lock(); }
bool try_lock() { return _Base::try_lock(); }
void unlock() { _Base::unlock(); }
template<typename _Rep, typename _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{
auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);
if (ratio_greater<__clock_t::period, _Period>())
++__rt;
return try_lock_until(__clock_t::now() + __rt);
}
// Shared ownership
void lock_shared() { _Base::lock_shared(); }
bool try_lock_shared() { return _Base::try_lock_shared(); }
void unlock_shared() { _Base::unlock_shared(); }
template<typename _Rep, typename _Period>
bool
try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rtime)
{
auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);
if (ratio_greater<__clock_t::period, _Period>())
++__rt;
return try_lock_shared_until(__clock_t::now() + __rt);
}
#if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
// Exclusive ownership
template<typename _Duration>
bool
try_lock_until(const chrono::time_point<chrono::system_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
int __ret = __glibcxx_rwlock_timedwrlock(&_M_rwlock, &__ts);
// On self-deadlock, we just fail to acquire the lock. Technically,
// the program violated the precondition.
if (__ret == ETIMEDOUT || __ret == EDEADLK)
return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
template<typename _Duration>
bool
try_lock_until(const chrono::time_point<chrono::steady_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
int __ret = pthread_rwlock_clockwrlock(&_M_rwlock, CLOCK_MONOTONIC,
&__ts);
// On self-deadlock, we just fail to acquire the lock. Technically,
// the program violated the precondition.
if (__ret == ETIMEDOUT || __ret == EDEADLK)
return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
#endif
template<typename _Clock, typename _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{
#if __cplusplus > 201703L
static_assert(chrono::is_clock_v<_Clock>);
#endif
// The user-supplied clock may not tick at the same rate as
// steady_clock, so we must loop in order to guarantee that
// the timeout has expired before returning false.
typename _Clock::time_point __now = _Clock::now();
do {
auto __rtime = __atime - __now;
if (try_lock_for(__rtime))
return true;
__now = _Clock::now();
} while (__atime > __now);
return false;
}
// Shared ownership
template<typename _Duration>
bool
try_lock_shared_until(const chrono::time_point<chrono::system_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
int __ret;
// Unlike for lock(), we are not allowed to throw an exception so if
// the maximum number of read locks has been exceeded, or we would
// deadlock, we just try to acquire the lock again (and will time out
// eventually).
// In cases where we would exceed the maximum number of read locks
// throughout the whole time until the timeout, we will fail to
// acquire the lock even if it would be logically free; however, this
// is allowed by the standard, and we made a "strong effort"
// (see C++14 30.4.1.4p26).
// For cases where the implementation detects a deadlock we
// intentionally block and timeout so that an early return isn't
// mistaken for a spurious failure, which might help users realise
// there is a deadlock.
do
__ret = __glibcxx_rwlock_timedrdlock(&_M_rwlock, &__ts);
while (__ret == EAGAIN || __ret == EDEADLK);
if (__ret == ETIMEDOUT)
return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK
template<typename _Duration>
bool
try_lock_shared_until(const chrono::time_point<chrono::steady_clock,
_Duration>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};
int __ret = pthread_rwlock_clockrdlock(&_M_rwlock, CLOCK_MONOTONIC,
&__ts);
// On self-deadlock, we just fail to acquire the lock. Technically,
// the program violated the precondition.
if (__ret == ETIMEDOUT || __ret == EDEADLK)
return false;
// Errors not handled: EINVAL
__glibcxx_assert(__ret == 0);
return true;
}
#endif
template<typename _Clock, typename _Duration>
bool
try_lock_shared_until(const chrono::time_point<_Clock,
_Duration>& __atime)
{
#if __cplusplus > 201703L
static_assert(chrono::is_clock_v<_Clock>);
#endif
// The user-supplied clock may not tick at the same rate as
// steady_clock, so we must loop in order to guarantee that
// the timeout has expired before returning false.
typename _Clock::time_point __now = _Clock::now();
do {
auto __rtime = __atime - __now;
if (try_lock_shared_for(__rtime))
return true;
__now = _Clock::now();
} while (__atime > __now);
return false;
}
#else // ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)
// ...
#endif // _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK
};
#endif // _GLIBCXX_HAS_GTHREADS
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777