bf1fc37bb4
For C++20 the wait_until members of mutexes and condition variables are required to be ill-formed if given a clock that doesn't meet the requirements for a clock type. To implement that requirement this patch adds static assertions using the chrono::is_clock trait, and defines that trait. To avoid expensive checks for the common cases, the trait (and associated variable template) are explicitly specialized for the standard clock types. This also moves the filesystem::__file_clock type from <filesystem> to <chrono>, so that chrono::file_clock and chrono::file_time can be defined in <chrono> as required. * include/bits/fs_fwd.h (filesystem::__file_clock): Move to ... * include/std/chrono (filesystem::__file_clock): Here. (filesystem::__file_clock::from_sys, filesystem::__file_clock::to_sys): Define public member functions for C++20. (is_clock, is_clock_v): Define traits for C++20. * include/std/condition_variable (condition_variable::wait_until): Add check for valid clock. * include/std/future (_State_baseV2::wait_until): Likewise. * include/std/mutex (__timed_mutex_impl::_M_try_lock_until): Likewise. * include/std/shared_mutex (shared_timed_mutex::try_lock_shared_until): Likewise. * include/std/thread (this_thread::sleep_until): Likewise. * testsuite/30_threads/condition_variable/members/2.cc: Qualify slow_clock with new namespace. * testsuite/30_threads/condition_variable/members/clock_neg.cc: New test. * testsuite/30_threads/condition_variable_any/members/clock_neg.cc: New test. * testsuite/30_threads/future/members/clock_neg.cc: New test. * testsuite/30_threads/recursive_timed_mutex/try_lock_until/3.cc: Qualify slow_clock with new namespace. * testsuite/30_threads/recursive_timed_mutex/try_lock_until/ clock_neg.cc: New test. * testsuite/30_threads/shared_future/members/clock_neg.cc: New test. * testsuite/30_threads/shared_lock/locking/clock_neg.cc: New test. * testsuite/30_threads/shared_timed_mutex/try_lock_until/clock_neg.cc: New test. * testsuite/30_threads/timed_mutex/try_lock_until/3.cc: Qualify slow_clock with new namespace. * testsuite/30_threads/timed_mutex/try_lock_until/4.cc: Likewise. * testsuite/30_threads/timed_mutex/try_lock_until/clock_neg.cc: New test. * testsuite/30_threads/unique_lock/locking/clock_neg.cc: New test. * testsuite/std/time/traits/is_clock.cc: New test. * testsuite/util/slow_clock.h (slow_clock): Move to __gnu_test namespace.
754 lines
19 KiB
C++
754 lines
19 KiB
C++
// <mutex> -*- C++ -*-
|
|
|
|
// Copyright (C) 2003-2020 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file include/mutex
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_MUTEX
|
|
#define _GLIBCXX_MUTEX 1
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus < 201103L
|
|
# include <bits/c++0x_warning.h>
|
|
#else
|
|
|
|
#include <tuple>
|
|
#include <chrono>
|
|
#include <exception>
|
|
#include <type_traits>
|
|
#include <system_error>
|
|
#include <bits/std_mutex.h>
|
|
#include <bits/unique_lock.h>
|
|
#if ! _GTHREAD_USE_MUTEX_TIMEDLOCK
|
|
# include <condition_variable>
|
|
# include <thread>
|
|
#endif
|
|
#ifndef _GLIBCXX_HAVE_TLS
|
|
# include <bits/std_function.h>
|
|
#endif
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
/**
|
|
* @addtogroup mutexes
|
|
* @{
|
|
*/
|
|
|
|
#ifdef _GLIBCXX_HAS_GTHREADS
|
|
|
|
// Common base class for std::recursive_mutex and std::recursive_timed_mutex
|
|
class __recursive_mutex_base
|
|
{
|
|
protected:
|
|
typedef __gthread_recursive_mutex_t __native_type;
|
|
|
|
__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
|
|
};
|
|
|
|
/// The standard recursive mutex type.
|
|
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; }
|
|
};
|
|
|
|
#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())
|
|
};
|
|
|
|
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())
|
|
};
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
/// The standard timed mutex type.
|
|
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); }
|
|
|
|
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_mutex_unlock(&_M_mutex);
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
/// recursive_timed_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
|
|
};
|
|
|
|
#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
|
|
|
|
/// timed_mutex
|
|
class timed_mutex
|
|
{
|
|
mutex _M_mut;
|
|
condition_variable _M_cv;
|
|
bool _M_locked = false;
|
|
|
|
public:
|
|
|
|
timed_mutex() = default;
|
|
~timed_mutex() { __glibcxx_assert( !_M_locked ); }
|
|
|
|
timed_mutex(const timed_mutex&) = delete;
|
|
timed_mutex& operator=(const timed_mutex&) = delete;
|
|
|
|
void
|
|
lock()
|
|
{
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
_M_cv.wait(__lk, [&]{ return !_M_locked; });
|
|
_M_locked = true;
|
|
}
|
|
|
|
bool
|
|
try_lock()
|
|
{
|
|
lock_guard<mutex> __lk(_M_mut);
|
|
if (_M_locked)
|
|
return false;
|
|
_M_locked = true;
|
|
return true;
|
|
}
|
|
|
|
template<typename _Rep, typename _Period>
|
|
bool
|
|
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
|
|
{
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
|
|
return false;
|
|
_M_locked = true;
|
|
return true;
|
|
}
|
|
|
|
template<typename _Clock, typename _Duration>
|
|
bool
|
|
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
|
|
{
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
|
|
return false;
|
|
_M_locked = true;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
unlock()
|
|
{
|
|
lock_guard<mutex> __lk(_M_mut);
|
|
__glibcxx_assert( _M_locked );
|
|
_M_locked = false;
|
|
_M_cv.notify_one();
|
|
}
|
|
};
|
|
|
|
/// recursive_timed_mutex
|
|
class recursive_timed_mutex
|
|
{
|
|
mutex _M_mut;
|
|
condition_variable _M_cv;
|
|
thread::id _M_owner;
|
|
unsigned _M_count = 0;
|
|
|
|
// Predicate type that tests whether the current thread can lock a mutex.
|
|
struct _Can_lock
|
|
{
|
|
// Returns true if the mutex is unlocked or is locked by _M_caller.
|
|
bool
|
|
operator()() const noexcept
|
|
{ return _M_mx->_M_count == 0 || _M_mx->_M_owner == _M_caller; }
|
|
|
|
const recursive_timed_mutex* _M_mx;
|
|
thread::id _M_caller;
|
|
};
|
|
|
|
public:
|
|
|
|
recursive_timed_mutex() = default;
|
|
~recursive_timed_mutex() { __glibcxx_assert( _M_count == 0 ); }
|
|
|
|
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
|
|
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
|
|
|
|
void
|
|
lock()
|
|
{
|
|
auto __id = this_thread::get_id();
|
|
_Can_lock __can_lock{this, __id};
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
_M_cv.wait(__lk, __can_lock);
|
|
if (_M_count == -1u)
|
|
__throw_system_error(EAGAIN); // [thread.timedmutex.recursive]/3
|
|
_M_owner = __id;
|
|
++_M_count;
|
|
}
|
|
|
|
bool
|
|
try_lock()
|
|
{
|
|
auto __id = this_thread::get_id();
|
|
_Can_lock __can_lock{this, __id};
|
|
lock_guard<mutex> __lk(_M_mut);
|
|
if (!__can_lock())
|
|
return false;
|
|
if (_M_count == -1u)
|
|
return false;
|
|
_M_owner = __id;
|
|
++_M_count;
|
|
return true;
|
|
}
|
|
|
|
template<typename _Rep, typename _Period>
|
|
bool
|
|
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
|
|
{
|
|
auto __id = this_thread::get_id();
|
|
_Can_lock __can_lock{this, __id};
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
if (!_M_cv.wait_for(__lk, __rtime, __can_lock))
|
|
return false;
|
|
if (_M_count == -1u)
|
|
return false;
|
|
_M_owner = __id;
|
|
++_M_count;
|
|
return true;
|
|
}
|
|
|
|
template<typename _Clock, typename _Duration>
|
|
bool
|
|
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
|
|
{
|
|
auto __id = this_thread::get_id();
|
|
_Can_lock __can_lock{this, __id};
|
|
unique_lock<mutex> __lk(_M_mut);
|
|
if (!_M_cv.wait_until(__lk, __atime, __can_lock))
|
|
return false;
|
|
if (_M_count == -1u)
|
|
return false;
|
|
_M_owner = __id;
|
|
++_M_count;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
unlock()
|
|
{
|
|
lock_guard<mutex> __lk(_M_mut);
|
|
__glibcxx_assert( _M_owner == this_thread::get_id() );
|
|
__glibcxx_assert( _M_count > 0 );
|
|
if (--_M_count == 0)
|
|
{
|
|
_M_owner = {};
|
|
_M_cv.notify_one();
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif
|
|
#endif // _GLIBCXX_HAS_GTHREADS
|
|
|
|
/// @cond undocumented
|
|
template<typename _Lock>
|
|
inline unique_lock<_Lock>
|
|
__try_to_lock(_Lock& __l)
|
|
{ return unique_lock<_Lock>{__l, try_to_lock}; }
|
|
|
|
template<int _Idx, bool _Continue = true>
|
|
struct __try_lock_impl
|
|
{
|
|
template<typename... _Lock>
|
|
static void
|
|
__do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
|
|
{
|
|
__idx = _Idx;
|
|
auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
|
|
if (__lock.owns_lock())
|
|
{
|
|
constexpr bool __cont = _Idx + 2 < sizeof...(_Lock);
|
|
using __try_locker = __try_lock_impl<_Idx + 1, __cont>;
|
|
__try_locker::__do_try_lock(__locks, __idx);
|
|
if (__idx == -1)
|
|
__lock.release();
|
|
}
|
|
}
|
|
};
|
|
|
|
template<int _Idx>
|
|
struct __try_lock_impl<_Idx, false>
|
|
{
|
|
template<typename... _Lock>
|
|
static void
|
|
__do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
|
|
{
|
|
__idx = _Idx;
|
|
auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
|
|
if (__lock.owns_lock())
|
|
{
|
|
__idx = -1;
|
|
__lock.release();
|
|
}
|
|
}
|
|
};
|
|
/// @endcond
|
|
|
|
/** @brief Generic try_lock.
|
|
* @param __l1 Meets Lockable requirements (try_lock() may throw).
|
|
* @param __l2 Meets Lockable requirements (try_lock() may throw).
|
|
* @param __l3 Meets Lockable requirements (try_lock() may throw).
|
|
* @return Returns -1 if all try_lock() calls return true. Otherwise returns
|
|
* a 0-based index corresponding to the argument that returned false.
|
|
* @post Either all arguments are locked, or none will be.
|
|
*
|
|
* Sequentially calls try_lock() on each argument.
|
|
*/
|
|
template<typename _Lock1, typename _Lock2, typename... _Lock3>
|
|
int
|
|
try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3)
|
|
{
|
|
int __idx;
|
|
auto __locks = std::tie(__l1, __l2, __l3...);
|
|
__try_lock_impl<0>::__do_try_lock(__locks, __idx);
|
|
return __idx;
|
|
}
|
|
|
|
/** @brief Generic lock.
|
|
* @param __l1 Meets Lockable requirements (try_lock() may throw).
|
|
* @param __l2 Meets Lockable requirements (try_lock() may throw).
|
|
* @param __l3 Meets Lockable requirements (try_lock() may throw).
|
|
* @throw An exception thrown by an argument's lock() or try_lock() member.
|
|
* @post All arguments are locked.
|
|
*
|
|
* All arguments are locked via a sequence of calls to lock(), try_lock()
|
|
* and unlock(). If the call exits via an exception any locks that were
|
|
* obtained will be released.
|
|
*/
|
|
template<typename _L1, typename _L2, typename... _L3>
|
|
void
|
|
lock(_L1& __l1, _L2& __l2, _L3&... __l3)
|
|
{
|
|
while (true)
|
|
{
|
|
using __try_locker = __try_lock_impl<0, sizeof...(_L3) != 0>;
|
|
unique_lock<_L1> __first(__l1);
|
|
int __idx;
|
|
auto __locks = std::tie(__l2, __l3...);
|
|
__try_locker::__do_try_lock(__locks, __idx);
|
|
if (__idx == -1)
|
|
{
|
|
__first.release();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __cplusplus >= 201703L
|
|
#define __cpp_lib_scoped_lock 201703
|
|
/** @brief A scoped lock type for multiple lockable objects.
|
|
*
|
|
* A scoped_lock controls mutex ownership within a scope, releasing
|
|
* ownership in the destructor.
|
|
*/
|
|
template<typename... _MutexTypes>
|
|
class scoped_lock
|
|
{
|
|
public:
|
|
explicit scoped_lock(_MutexTypes&... __m) : _M_devices(std::tie(__m...))
|
|
{ std::lock(__m...); }
|
|
|
|
explicit scoped_lock(adopt_lock_t, _MutexTypes&... __m) noexcept
|
|
: _M_devices(std::tie(__m...))
|
|
{ } // calling thread owns mutex
|
|
|
|
~scoped_lock()
|
|
{ std::apply([](auto&... __m) { (__m.unlock(), ...); }, _M_devices); }
|
|
|
|
scoped_lock(const scoped_lock&) = delete;
|
|
scoped_lock& operator=(const scoped_lock&) = delete;
|
|
|
|
private:
|
|
tuple<_MutexTypes&...> _M_devices;
|
|
};
|
|
|
|
template<>
|
|
class scoped_lock<>
|
|
{
|
|
public:
|
|
explicit scoped_lock() = default;
|
|
explicit scoped_lock(adopt_lock_t) noexcept { }
|
|
~scoped_lock() = default;
|
|
|
|
scoped_lock(const scoped_lock&) = delete;
|
|
scoped_lock& operator=(const scoped_lock&) = delete;
|
|
};
|
|
|
|
template<typename _Mutex>
|
|
class scoped_lock<_Mutex>
|
|
{
|
|
public:
|
|
using mutex_type = _Mutex;
|
|
|
|
explicit scoped_lock(mutex_type& __m) : _M_device(__m)
|
|
{ _M_device.lock(); }
|
|
|
|
explicit scoped_lock(adopt_lock_t, mutex_type& __m) noexcept
|
|
: _M_device(__m)
|
|
{ } // calling thread owns mutex
|
|
|
|
~scoped_lock()
|
|
{ _M_device.unlock(); }
|
|
|
|
scoped_lock(const scoped_lock&) = delete;
|
|
scoped_lock& operator=(const scoped_lock&) = delete;
|
|
|
|
private:
|
|
mutex_type& _M_device;
|
|
};
|
|
#endif // C++17
|
|
|
|
#ifdef _GLIBCXX_HAS_GTHREADS
|
|
/// Flag type used by std::call_once
|
|
struct once_flag
|
|
{
|
|
private:
|
|
typedef __gthread_once_t __native_type;
|
|
__native_type _M_once = __GTHREAD_ONCE_INIT;
|
|
|
|
public:
|
|
/// Constructor
|
|
constexpr once_flag() noexcept = default;
|
|
|
|
/// Deleted copy constructor
|
|
once_flag(const once_flag&) = delete;
|
|
/// Deleted assignment operator
|
|
once_flag& operator=(const once_flag&) = delete;
|
|
|
|
template<typename _Callable, typename... _Args>
|
|
friend void
|
|
call_once(once_flag& __once, _Callable&& __f, _Args&&... __args);
|
|
};
|
|
|
|
/// @cond undocumented
|
|
#ifdef _GLIBCXX_HAVE_TLS
|
|
extern __thread void* __once_callable;
|
|
extern __thread void (*__once_call)();
|
|
#else
|
|
extern function<void()> __once_functor;
|
|
|
|
extern void
|
|
__set_once_functor_lock_ptr(unique_lock<mutex>*);
|
|
|
|
extern mutex&
|
|
__get_once_mutex();
|
|
#endif
|
|
|
|
extern "C" void __once_proxy(void);
|
|
/// @endcond
|
|
|
|
/// Invoke a callable and synchronize with other calls using the same flag
|
|
template<typename _Callable, typename... _Args>
|
|
void
|
|
call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
|
|
{
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// 2442. call_once() shouldn't DECAY_COPY()
|
|
auto __callable = [&] {
|
|
std::__invoke(std::forward<_Callable>(__f),
|
|
std::forward<_Args>(__args)...);
|
|
};
|
|
#ifdef _GLIBCXX_HAVE_TLS
|
|
__once_callable = std::__addressof(__callable);
|
|
__once_call = []{ (*(decltype(__callable)*)__once_callable)(); };
|
|
#else
|
|
unique_lock<mutex> __functor_lock(__get_once_mutex());
|
|
__once_functor = __callable;
|
|
__set_once_functor_lock_ptr(&__functor_lock);
|
|
#endif
|
|
|
|
int __e = __gthread_once(&__once._M_once, &__once_proxy);
|
|
|
|
#ifndef _GLIBCXX_HAVE_TLS
|
|
if (__functor_lock)
|
|
__set_once_functor_lock_ptr(0);
|
|
#endif
|
|
|
|
#ifdef __clang_analyzer__
|
|
// PR libstdc++/82481
|
|
__once_callable = nullptr;
|
|
__once_call = nullptr;
|
|
#endif
|
|
|
|
if (__e)
|
|
__throw_system_error(__e);
|
|
}
|
|
#endif // _GLIBCXX_HAS_GTHREADS
|
|
|
|
// @} group mutexes
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace
|
|
|
|
#endif // C++11
|
|
|
|
#endif // _GLIBCXX_MUTEX
|