| // <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 |