|  | // <mutex> -*- C++ -*- | 
|  |  | 
|  | // Copyright (C) 2003-2025 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 | 
|  |  | 
|  | #ifdef _GLIBCXX_SYSHDR | 
|  | #pragma GCC system_header | 
|  | #endif | 
|  |  | 
|  | #include <bits/requires_hosted.h> // concurrency | 
|  |  | 
|  | #if __cplusplus < 201103L | 
|  | # include <bits/c++0x_warning.h> | 
|  | #else | 
|  |  | 
|  | #include <tuple>	      // std::tuple | 
|  | #include <type_traits>        // is_same_v | 
|  | #include <errno.h>	      // EAGAIN, EDEADLK | 
|  | #include <bits/chrono.h>      // duration, time_point, is_clock_v | 
|  | #include <bits/functexcept.h> // __throw_system_error | 
|  | #include <bits/invoke.h>      // __invoke | 
|  | #include <bits/move.h>	      // std::forward | 
|  | #include <bits/std_mutex.h> | 
|  | #include <bits/unique_lock.h> | 
|  | #if ! _GTHREAD_USE_MUTEX_TIMEDLOCK | 
|  | # include <condition_variable> | 
|  | # include <thread> | 
|  | #endif | 
|  | #include <ext/atomicity.h>     // __gnu_cxx::__is_single_threaded | 
|  |  | 
|  | #if defined _GLIBCXX_HAS_GTHREADS && ! defined _GLIBCXX_HAVE_TLS | 
|  | # include <bits/std_function.h>  // std::function | 
|  | #endif | 
|  |  | 
|  | #define __glibcxx_want_scoped_lock | 
|  | #include <bits/version.h> | 
|  |  | 
|  | namespace std _GLIBCXX_VISIBILITY(default) | 
|  | { | 
|  | _GLIBCXX_BEGIN_NAMESPACE_VERSION | 
|  |  | 
|  | /** | 
|  | * @addtogroup mutexes | 
|  | * @{ | 
|  | */ | 
|  |  | 
|  | #ifdef _GLIBCXX_HAS_GTHREADS | 
|  | /// @cond undocumented | 
|  |  | 
|  | // 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 | 
|  | }; | 
|  | /// @endcond | 
|  |  | 
|  | /** The standard recursive mutex type. | 
|  | * | 
|  | * A recursive mutex can be locked more than once by the same thread. | 
|  | * Other threads cannot lock the mutex until the owning thread unlocks it | 
|  | * as many times as it was locked. | 
|  | * | 
|  | * @headerfile mutex | 
|  | * @since C++11 | 
|  | */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | _GLIBCXX_NODISCARD | 
|  | 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 | 
|  | /// @cond undocumented | 
|  |  | 
|  | 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; | 
|  | } | 
|  | }; | 
|  | /// @endcond | 
|  |  | 
|  | /** The standard timed mutex type. | 
|  | * | 
|  | * A non-recursive mutex that supports a timeout when trying to acquire the | 
|  | * lock. | 
|  | * | 
|  | * @headerfile mutex | 
|  | * @since C++11 | 
|  | */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | _GLIBCXX_NODISCARD | 
|  | bool | 
|  | try_lock() noexcept | 
|  | { | 
|  | // XXX EINVAL, EAGAIN, EBUSY | 
|  | return !__gthread_mutex_trylock(&_M_mutex); | 
|  | } | 
|  |  | 
|  | template <class _Rep, class _Period> | 
|  | _GLIBCXX_NODISCARD | 
|  | bool | 
|  | try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) | 
|  | { return _M_try_lock_for(__rtime); } | 
|  |  | 
|  | template <class _Clock, class _Duration> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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 | 
|  | }; | 
|  |  | 
|  | /** The standard recursive timed mutex type. | 
|  | * | 
|  | * A recursive mutex that supports a timeout when trying to acquire the | 
|  | * lock. A recursive mutex can be locked more than once by the same thread. | 
|  | * Other threads cannot lock the mutex until the owning thread unlocks it | 
|  | * as many times as it was locked. | 
|  | * | 
|  | * @headerfile mutex | 
|  | * @since C++11 | 
|  | */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | _GLIBCXX_NODISCARD | 
|  | bool | 
|  | try_lock() noexcept | 
|  | { | 
|  | // XXX EINVAL, EAGAIN, EBUSY | 
|  | return !__gthread_recursive_mutex_trylock(&_M_mutex); | 
|  | } | 
|  |  | 
|  | template <class _Rep, class _Period> | 
|  | _GLIBCXX_NODISCARD | 
|  | bool | 
|  | try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) | 
|  | { return _M_try_lock_for(__rtime); } | 
|  |  | 
|  | template <class _Clock, class _Duration> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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; | 
|  | } | 
|  |  | 
|  | _GLIBCXX_NODISCARD | 
|  | bool | 
|  | try_lock() | 
|  | { | 
|  | lock_guard<mutex> __lk(_M_mut); | 
|  | if (_M_locked) | 
|  | return false; | 
|  | _M_locked = true; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<typename _Rep, typename _Period> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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; | 
|  | } | 
|  |  | 
|  | _GLIBCXX_NODISCARD | 
|  | 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> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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> | 
|  | _GLIBCXX_NODISCARD | 
|  | 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 | 
|  | namespace __detail | 
|  | { | 
|  | // Lock the last lockable, after all previous ones are locked. | 
|  | template<typename _Lockable> | 
|  | inline int | 
|  | __try_lock_impl(_Lockable& __l) | 
|  | { | 
|  | if (unique_lock<_Lockable> __lock{__l, try_to_lock}) | 
|  | { | 
|  | __lock.release(); | 
|  | return -1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Lock each lockable in turn. | 
|  | // Use iteration if all lockables are the same type, recursion otherwise. | 
|  | template<typename _L0, typename... _Lockables> | 
|  | inline int | 
|  | __try_lock_impl(_L0& __l0, _Lockables&... __lockables) | 
|  | { | 
|  | #if __cplusplus >= 201703L | 
|  | if constexpr ((is_same_v<_L0, _Lockables> && ...)) | 
|  | { | 
|  | constexpr int _Np = 1 + sizeof...(_Lockables); | 
|  | unique_lock<_L0> __locks[_Np] = { | 
|  | {__l0, defer_lock}, {__lockables, defer_lock}... | 
|  | }; | 
|  | for (int __i = 0; __i < _Np; ++__i) | 
|  | { | 
|  | if (!__locks[__i].try_lock()) | 
|  | { | 
|  | const int __failed = __i; | 
|  | while (__i--) | 
|  | __locks[__i].unlock(); | 
|  | return __failed; | 
|  | } | 
|  | } | 
|  | for (auto& __l : __locks) | 
|  | __l.release(); | 
|  | return -1; | 
|  | } | 
|  | else | 
|  | #endif | 
|  | if (unique_lock<_L0> __lock{__l0, try_to_lock}) | 
|  | { | 
|  | int __idx = __detail::__try_lock_impl(__lockables...); | 
|  | if (__idx == -1) | 
|  | { | 
|  | __lock.release(); | 
|  | return -1; | 
|  | } | 
|  | return __idx + 1; | 
|  | } | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | } // namespace __detail | 
|  | /// @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 _L1, typename _L2, typename... _L3> | 
|  | _GLIBCXX_NODISCARD | 
|  | inline int | 
|  | try_lock(_L1& __l1, _L2& __l2, _L3&... __l3) | 
|  | { | 
|  | return __detail::__try_lock_impl(__l1, __l2, __l3...); | 
|  | } | 
|  |  | 
|  | /// @cond undocumented | 
|  | namespace __detail | 
|  | { | 
|  | // This function can recurse up to N levels deep, for N = 1+sizeof...(L1). | 
|  | // On each recursion the lockables are rotated left one position, | 
|  | // e.g. depth 0: l0, l1, l2; depth 1: l1, l2, l0; depth 2: l2, l0, l1. | 
|  | // When a call to l_i.try_lock() fails it recurses/returns to depth=i | 
|  | // so that l_i is the first argument, and then blocks until l_i is locked. | 
|  | template<typename _L0, typename... _L1> | 
|  | void | 
|  | __lock_impl(int& __i, int __depth, _L0& __l0, _L1&... __l1) | 
|  | { | 
|  | while (__i >= __depth) | 
|  | { | 
|  | if (__i == __depth) | 
|  | { | 
|  | int __failed = 1; // index that couldn't be locked | 
|  | { | 
|  | unique_lock<_L0> __first(__l0); | 
|  | __failed += __detail::__try_lock_impl(__l1...); | 
|  | if (!__failed) | 
|  | { | 
|  | __i = -1; // finished | 
|  | __first.release(); | 
|  | return; | 
|  | } | 
|  | } | 
|  | #if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD | 
|  | __gthread_yield(); | 
|  | #endif | 
|  | constexpr auto __n = 1 + sizeof...(_L1); | 
|  | __i = (__depth + __failed) % __n; | 
|  | } | 
|  | else // rotate left until l_i is first. | 
|  | __detail::__lock_impl(__i, __depth + 1, __l1..., __l0); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace __detail | 
|  | /// @endcond | 
|  |  | 
|  | /** @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 this function 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) | 
|  | { | 
|  | #if __cplusplus >= 201703L | 
|  | if constexpr (is_same_v<_L1, _L2> && (is_same_v<_L1, _L3> && ...)) | 
|  | { | 
|  | constexpr int _Np = 2 + sizeof...(_L3); | 
|  | unique_lock<_L1> __locks[] = { | 
|  | {__l1, defer_lock}, {__l2, defer_lock}, {__l3, defer_lock}... | 
|  | }; | 
|  | int __first = 0; | 
|  | do { | 
|  | __locks[__first].lock(); | 
|  | for (int __j = 1; __j < _Np; ++__j) | 
|  | { | 
|  | const int __idx = (__first + __j) % _Np; | 
|  | if (!__locks[__idx].try_lock()) | 
|  | { | 
|  | for (int __k = __j; __k != 0; --__k) | 
|  | __locks[(__first + __k - 1) % _Np].unlock(); | 
|  | __first = __idx; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } while (!__locks[__first].owns_lock()); | 
|  |  | 
|  | for (auto& __l : __locks) | 
|  | __l.release(); | 
|  | } | 
|  | else | 
|  | #endif | 
|  | { | 
|  | int __i = 0; | 
|  | __detail::__lock_impl(__i, 0, __l1, __l2, __l3...); | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef __cpp_lib_scoped_lock // C++ >= 17 | 
|  | /** @brief A scoped lock type for multiple lockable objects. | 
|  | * | 
|  | * A scoped_lock controls mutex ownership within a scope, releasing | 
|  | * ownership in the destructor. | 
|  | * | 
|  | * @headerfile mutex | 
|  | * @since C++17 | 
|  | */ | 
|  | template<typename... _MutexTypes> | 
|  | class scoped_lock | 
|  | { | 
|  | public: | 
|  |  | 
|  | [[nodiscard]] | 
|  | explicit scoped_lock(_MutexTypes&... __m) : _M_devices(std::tie(__m...)) | 
|  | { std::lock(__m...); } | 
|  |  | 
|  | [[nodiscard]] | 
|  | 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; | 
|  |  | 
|  | [[nodiscard]] | 
|  | explicit scoped_lock(mutex_type& __m) : _M_device(__m) | 
|  | { _M_device.lock(); } | 
|  |  | 
|  | [[nodiscard]] | 
|  | 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 // __cpp_lib_scoped_lock | 
|  |  | 
|  | #ifdef _GLIBCXX_HAS_GTHREADS | 
|  | /// Flag type used by std::call_once | 
|  | struct once_flag | 
|  | { | 
|  | constexpr once_flag() noexcept = default; | 
|  |  | 
|  | /// Deleted copy constructor | 
|  | once_flag(const once_flag&) = delete; | 
|  | /// Deleted assignment operator | 
|  | once_flag& operator=(const once_flag&) = delete; | 
|  |  | 
|  | private: | 
|  | // For gthreads targets a pthread_once_t is used with pthread_once, but | 
|  | // for most targets this doesn't work correctly for exceptional executions. | 
|  | __gthread_once_t _M_once = __GTHREAD_ONCE_INIT; | 
|  |  | 
|  | struct _Prepare_execution; | 
|  |  | 
|  | template<typename _Callable, typename... _Args> | 
|  | friend void | 
|  | call_once(once_flag& __once, _Callable&& __f, _Args&&... __args); | 
|  | }; | 
|  |  | 
|  | /// @cond undocumented | 
|  | # ifdef _GLIBCXX_HAVE_TLS | 
|  | // If TLS is available use thread-local state for the type-erased callable | 
|  | // that is being run by std::call_once in the current thread. | 
|  | extern __thread void* __once_callable; | 
|  | extern __thread void (*__once_call)(); | 
|  |  | 
|  | // RAII type to set up state for pthread_once call. | 
|  | struct once_flag::_Prepare_execution | 
|  | { | 
|  | template<typename _Callable> | 
|  | explicit | 
|  | _Prepare_execution(_Callable& __c) | 
|  | { | 
|  | // Store address in thread-local pointer: | 
|  | __once_callable = std::__addressof(__c); | 
|  | // Trampoline function to invoke the closure via thread-local pointer: | 
|  | __once_call = [] { (*static_cast<_Callable*>(__once_callable))(); }; | 
|  | } | 
|  |  | 
|  | ~_Prepare_execution() | 
|  | { | 
|  | // PR libstdc++/82481 | 
|  | __once_callable = nullptr; | 
|  | __once_call = nullptr; | 
|  | } | 
|  |  | 
|  | _Prepare_execution(const _Prepare_execution&) = delete; | 
|  | _Prepare_execution& operator=(const _Prepare_execution&) = delete; | 
|  | }; | 
|  |  | 
|  | # else | 
|  | // Without TLS use a global std::mutex and store the callable in a | 
|  | // global std::function. | 
|  | extern function<void()> __once_functor; | 
|  |  | 
|  | extern void | 
|  | __set_once_functor_lock_ptr(unique_lock<mutex>*); | 
|  |  | 
|  | extern mutex& | 
|  | __get_once_mutex(); | 
|  |  | 
|  | // RAII type to set up state for pthread_once call. | 
|  | struct once_flag::_Prepare_execution | 
|  | { | 
|  | template<typename _Callable> | 
|  | explicit | 
|  | _Prepare_execution(_Callable& __c) | 
|  | { | 
|  | // Store the callable in the global std::function | 
|  | __once_functor = __c; | 
|  | __set_once_functor_lock_ptr(&_M_functor_lock); | 
|  | } | 
|  |  | 
|  | ~_Prepare_execution() | 
|  | { | 
|  | if (_M_functor_lock) | 
|  | __set_once_functor_lock_ptr(nullptr); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // XXX This deadlocks if used recursively (PR 97949) | 
|  | unique_lock<mutex> _M_functor_lock{__get_once_mutex()}; | 
|  |  | 
|  | _Prepare_execution(const _Prepare_execution&) = delete; | 
|  | _Prepare_execution& operator=(const _Prepare_execution&) = delete; | 
|  | }; | 
|  | # endif | 
|  | /// @endcond | 
|  |  | 
|  | // This function is passed to pthread_once by std::call_once. | 
|  | // It runs __once_call() or __once_functor(). | 
|  | extern "C" void __once_proxy(void); | 
|  |  | 
|  | /// 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) | 
|  | { | 
|  | // Closure type that runs the function | 
|  | auto __callable = [&] { | 
|  | std::__invoke(std::forward<_Callable>(__f), | 
|  | std::forward<_Args>(__args)...); | 
|  | }; | 
|  |  | 
|  | once_flag::_Prepare_execution __exec(__callable); | 
|  |  | 
|  | // XXX pthread_once does not reset the flag if an exception is thrown. | 
|  | if (int __e = __gthread_once(&__once._M_once, &__once_proxy)) | 
|  | __throw_system_error(__e); | 
|  | } | 
|  |  | 
|  | #else // _GLIBCXX_HAS_GTHREADS | 
|  |  | 
|  | /// Flag type used by std::call_once | 
|  | struct once_flag | 
|  | { | 
|  | constexpr once_flag() noexcept = default; | 
|  |  | 
|  | /// Deleted copy constructor | 
|  | once_flag(const once_flag&) = delete; | 
|  | /// Deleted assignment operator | 
|  | once_flag& operator=(const once_flag&) = delete; | 
|  |  | 
|  | private: | 
|  | // There are two different std::once_flag interfaces, abstracting four | 
|  | // different implementations. | 
|  | // The single-threaded interface uses the _M_activate() and _M_finish(bool) | 
|  | // functions, which start and finish an active execution respectively. | 
|  | // See [thread.once.callonce] in C++11 for the definition of | 
|  | // active/passive/returning/exceptional executions. | 
|  | enum _Bits : int { _Init = 0, _Active = 1, _Done = 2 }; | 
|  |  | 
|  | int _M_once = _Bits::_Init; | 
|  |  | 
|  | // Check to see if all executions will be passive now. | 
|  | bool | 
|  | _M_passive() const noexcept; | 
|  |  | 
|  | // Attempts to begin an active execution. | 
|  | bool _M_activate(); | 
|  |  | 
|  | // Must be called to complete an active execution. | 
|  | // The argument is true if the active execution was a returning execution, | 
|  | // false if it was an exceptional execution. | 
|  | void _M_finish(bool __returning) noexcept; | 
|  |  | 
|  | // RAII helper to call _M_finish. | 
|  | struct _Active_execution | 
|  | { | 
|  | explicit _Active_execution(once_flag& __flag) : _M_flag(__flag) { } | 
|  |  | 
|  | ~_Active_execution() { _M_flag._M_finish(_M_returning); } | 
|  |  | 
|  | _Active_execution(const _Active_execution&) = delete; | 
|  | _Active_execution& operator=(const _Active_execution&) = delete; | 
|  |  | 
|  | once_flag& _M_flag; | 
|  | bool _M_returning = false; | 
|  | }; | 
|  |  | 
|  | template<typename _Callable, typename... _Args> | 
|  | friend void | 
|  | call_once(once_flag& __once, _Callable&& __f, _Args&&... __args); | 
|  | }; | 
|  |  | 
|  | // Inline definitions of std::once_flag members for single-threaded targets. | 
|  |  | 
|  | inline bool | 
|  | once_flag::_M_passive() const noexcept | 
|  | { return _M_once == _Bits::_Done; } | 
|  |  | 
|  | inline bool | 
|  | once_flag::_M_activate() | 
|  | { | 
|  | if (_M_once == _Bits::_Init) [[__likely__]] | 
|  | { | 
|  | _M_once = _Bits::_Active; | 
|  | return true; | 
|  | } | 
|  | else if (_M_passive()) // Caller should have checked this already. | 
|  | return false; | 
|  | else | 
|  | __throw_system_error(EDEADLK); | 
|  | } | 
|  |  | 
|  | inline void | 
|  | once_flag::_M_finish(bool __returning) noexcept | 
|  | { _M_once = __returning ? _Bits::_Done : _Bits::_Init; } | 
|  |  | 
|  | /// Invoke a callable and synchronize with other calls using the same flag | 
|  | template<typename _Callable, typename... _Args> | 
|  | inline void | 
|  | call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) | 
|  | { | 
|  | if (__once._M_passive()) | 
|  | return; | 
|  | else if (__once._M_activate()) | 
|  | { | 
|  | once_flag::_Active_execution __exec(__once); | 
|  |  | 
|  | // _GLIBCXX_RESOLVE_LIB_DEFECTS | 
|  | // 2442. call_once() shouldn't DECAY_COPY() | 
|  | std::__invoke(std::forward<_Callable>(__f), | 
|  | std::forward<_Args>(__args)...); | 
|  |  | 
|  | // __f(__args...) did not throw | 
|  | __exec._M_returning = true; | 
|  | } | 
|  | } | 
|  | #endif // _GLIBCXX_HAS_GTHREADS | 
|  |  | 
|  | /// @} group mutexes | 
|  | _GLIBCXX_END_NAMESPACE_VERSION | 
|  | } // namespace | 
|  |  | 
|  | #endif // C++11 | 
|  |  | 
|  | #endif // _GLIBCXX_MUTEX |