libstdc++
mutex
Go to the documentation of this file.
00001 // <mutex> -*- C++ -*-
00002 
00003 // Copyright (C) 2003-2017 Free Software Foundation, Inc.
00004 //
00005 // This file is part of the GNU ISO C++ Library.  This library is free
00006 // software; you can redistribute it and/or modify it under the
00007 // terms of the GNU General Public License as published by the
00008 // Free Software Foundation; either version 3, or (at your option)
00009 // any later version.
00010 
00011 // This library is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 
00016 // Under Section 7 of GPL version 3, you are granted additional
00017 // permissions described in the GCC Runtime Library Exception, version
00018 // 3.1, as published by the Free Software Foundation.
00019 
00020 // You should have received a copy of the GNU General Public License and
00021 // a copy of the GCC Runtime Library Exception along with this program;
00022 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
00023 // <http://www.gnu.org/licenses/>.
00024 
00025 /** @file include/mutex
00026  *  This is a Standard C++ Library header.
00027  */
00028 
00029 #ifndef _GLIBCXX_MUTEX
00030 #define _GLIBCXX_MUTEX 1
00031 
00032 #pragma GCC system_header
00033 
00034 #if __cplusplus < 201103L
00035 # include <bits/c++0x_warning.h>
00036 #else
00037 
00038 #include <tuple>
00039 #include <chrono>
00040 #include <exception>
00041 #include <type_traits>
00042 #include <system_error>
00043 #include <bits/std_mutex.h>
00044 #if ! _GTHREAD_USE_MUTEX_TIMEDLOCK
00045 # include <condition_variable>
00046 # include <thread>
00047 #endif
00048 #ifndef _GLIBCXX_HAVE_TLS
00049 # include <bits/std_function.h>
00050 #endif
00051 
00052 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
00053 
00054 namespace std _GLIBCXX_VISIBILITY(default)
00055 {
00056 _GLIBCXX_BEGIN_NAMESPACE_VERSION
00057 
00058   /**
00059    * @ingroup mutexes
00060    * @{
00061    */
00062 
00063 #ifdef _GLIBCXX_HAS_GTHREADS
00064 
00065   // Common base class for std::recursive_mutex and std::recursive_timed_mutex
00066   class __recursive_mutex_base
00067   {
00068   protected:
00069     typedef __gthread_recursive_mutex_t         __native_type;
00070 
00071     __recursive_mutex_base(const __recursive_mutex_base&) = delete;
00072     __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
00073 
00074 #ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
00075     __native_type  _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;
00076 
00077     __recursive_mutex_base() = default;
00078 #else
00079     __native_type  _M_mutex;
00080 
00081     __recursive_mutex_base()
00082     {
00083       // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
00084       __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
00085     }
00086 
00087     ~__recursive_mutex_base()
00088     { __gthread_recursive_mutex_destroy(&_M_mutex); }
00089 #endif
00090   };
00091 
00092   /// The standard recursive mutex type.
00093   class recursive_mutex : private __recursive_mutex_base
00094   {
00095   public:
00096     typedef __native_type*                      native_handle_type;
00097 
00098     recursive_mutex() = default;
00099     ~recursive_mutex() = default;
00100 
00101     recursive_mutex(const recursive_mutex&) = delete;
00102     recursive_mutex& operator=(const recursive_mutex&) = delete;
00103 
00104     void
00105     lock()
00106     {
00107       int __e = __gthread_recursive_mutex_lock(&_M_mutex);
00108 
00109       // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
00110       if (__e)
00111         __throw_system_error(__e);
00112     }
00113 
00114     bool
00115     try_lock() noexcept
00116     {
00117       // XXX EINVAL, EAGAIN, EBUSY
00118       return !__gthread_recursive_mutex_trylock(&_M_mutex);
00119     }
00120 
00121     void
00122     unlock()
00123     {
00124       // XXX EINVAL, EAGAIN, EBUSY
00125       __gthread_recursive_mutex_unlock(&_M_mutex);
00126     }
00127 
00128     native_handle_type
00129     native_handle() noexcept
00130     { return &_M_mutex; }
00131   };
00132 
00133 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
00134   template<typename _Derived>
00135     class __timed_mutex_impl
00136     {
00137     protected:
00138       typedef chrono::high_resolution_clock     __clock_t;
00139 
00140       template<typename _Rep, typename _Period>
00141         bool
00142         _M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
00143         {
00144           using chrono::steady_clock;
00145           auto __rt = chrono::duration_cast<steady_clock::duration>(__rtime);
00146           if (ratio_greater<steady_clock::period, _Period>())
00147             ++__rt;
00148           return _M_try_lock_until(steady_clock::now() + __rt);
00149         }
00150 
00151       template<typename _Duration>
00152         bool
00153         _M_try_lock_until(const chrono::time_point<__clock_t,
00154                                                    _Duration>& __atime)
00155         {
00156           auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
00157           auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
00158 
00159           __gthread_time_t __ts = {
00160             static_cast<std::time_t>(__s.time_since_epoch().count()),
00161             static_cast<long>(__ns.count())
00162           };
00163 
00164           return static_cast<_Derived*>(this)->_M_timedlock(__ts);
00165         }
00166 
00167       template<typename _Clock, typename _Duration>
00168         bool
00169         _M_try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
00170         {
00171           auto __rtime = __atime - _Clock::now();
00172           return _M_try_lock_until(__clock_t::now() + __rtime);
00173         }
00174     };
00175 
00176   /// The standard timed mutex type.
00177   class timed_mutex
00178   : private __mutex_base, public __timed_mutex_impl<timed_mutex>
00179   {
00180   public:
00181     typedef __native_type*                      native_handle_type;
00182 
00183     timed_mutex() = default;
00184     ~timed_mutex() = default;
00185 
00186     timed_mutex(const timed_mutex&) = delete;
00187     timed_mutex& operator=(const timed_mutex&) = delete;
00188 
00189     void
00190     lock()
00191     {
00192       int __e = __gthread_mutex_lock(&_M_mutex);
00193 
00194       // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
00195       if (__e)
00196         __throw_system_error(__e);
00197     }
00198 
00199     bool
00200     try_lock() noexcept
00201     {
00202       // XXX EINVAL, EAGAIN, EBUSY
00203       return !__gthread_mutex_trylock(&_M_mutex);
00204     }
00205 
00206     template <class _Rep, class _Period>
00207       bool
00208       try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
00209       { return _M_try_lock_for(__rtime); }
00210 
00211     template <class _Clock, class _Duration>
00212       bool
00213       try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
00214       { return _M_try_lock_until(__atime); }
00215 
00216     void
00217     unlock()
00218     {
00219       // XXX EINVAL, EAGAIN, EBUSY
00220       __gthread_mutex_unlock(&_M_mutex);
00221     }
00222 
00223     native_handle_type
00224     native_handle() noexcept
00225     { return &_M_mutex; }
00226 
00227     private:
00228       friend class __timed_mutex_impl<timed_mutex>;
00229 
00230       bool
00231       _M_timedlock(const __gthread_time_t& __ts)
00232       { return !__gthread_mutex_timedlock(&_M_mutex, &__ts); }
00233   };
00234 
00235   /// recursive_timed_mutex
00236   class recursive_timed_mutex
00237   : private __recursive_mutex_base,
00238     public __timed_mutex_impl<recursive_timed_mutex>
00239   {
00240   public:
00241     typedef __native_type*                      native_handle_type;
00242 
00243     recursive_timed_mutex() = default;
00244     ~recursive_timed_mutex() = default;
00245 
00246     recursive_timed_mutex(const recursive_timed_mutex&) = delete;
00247     recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
00248 
00249     void
00250     lock()
00251     {
00252       int __e = __gthread_recursive_mutex_lock(&_M_mutex);
00253 
00254       // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
00255       if (__e)
00256         __throw_system_error(__e);
00257     }
00258 
00259     bool
00260     try_lock() noexcept
00261     {
00262       // XXX EINVAL, EAGAIN, EBUSY
00263       return !__gthread_recursive_mutex_trylock(&_M_mutex);
00264     }
00265 
00266     template <class _Rep, class _Period>
00267       bool
00268       try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
00269       { return _M_try_lock_for(__rtime); }
00270 
00271     template <class _Clock, class _Duration>
00272       bool
00273       try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
00274       { return _M_try_lock_until(__atime); }
00275 
00276     void
00277     unlock()
00278     {
00279       // XXX EINVAL, EAGAIN, EBUSY
00280       __gthread_recursive_mutex_unlock(&_M_mutex);
00281     }
00282 
00283     native_handle_type
00284     native_handle() noexcept
00285     { return &_M_mutex; }
00286 
00287     private:
00288       friend class __timed_mutex_impl<recursive_timed_mutex>;
00289 
00290       bool
00291       _M_timedlock(const __gthread_time_t& __ts)
00292       { return !__gthread_recursive_mutex_timedlock(&_M_mutex, &__ts); }
00293   };
00294 
00295 #else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
00296 
00297   /// timed_mutex
00298   class timed_mutex
00299   {
00300     mutex               _M_mut;
00301     condition_variable  _M_cv;
00302     bool                _M_locked = false;
00303 
00304   public:
00305 
00306     timed_mutex() = default;
00307     ~timed_mutex() { __glibcxx_assert( !_M_locked ); }
00308 
00309     timed_mutex(const timed_mutex&) = delete;
00310     timed_mutex& operator=(const timed_mutex&) = delete;
00311 
00312     void
00313     lock()
00314     {
00315       unique_lock<mutex> __lk(_M_mut);
00316       _M_cv.wait(__lk, [&]{ return !_M_locked; });
00317       _M_locked = true;
00318     }
00319 
00320     bool
00321     try_lock()
00322     {
00323       lock_guard<mutex> __lk(_M_mut);
00324       if (_M_locked)
00325         return false;
00326       _M_locked = true;
00327       return true;
00328     }
00329 
00330     template<typename _Rep, typename _Period>
00331       bool
00332       try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
00333       {
00334         unique_lock<mutex> __lk(_M_mut);
00335         if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
00336           return false;
00337         _M_locked = true;
00338         return true;
00339       }
00340 
00341     template<typename _Clock, typename _Duration>
00342       bool
00343       try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
00344       {
00345         unique_lock<mutex> __lk(_M_mut);
00346         if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
00347           return false;
00348         _M_locked = true;
00349         return true;
00350       }
00351 
00352     void
00353     unlock()
00354     {
00355       lock_guard<mutex> __lk(_M_mut);
00356       __glibcxx_assert( _M_locked );
00357       _M_locked = false;
00358       _M_cv.notify_one();
00359     }
00360   };
00361 
00362   /// recursive_timed_mutex
00363   class recursive_timed_mutex
00364   {
00365     mutex               _M_mut;
00366     condition_variable  _M_cv;
00367     thread::id          _M_owner;
00368     unsigned            _M_count = 0;
00369 
00370     // Predicate type that tests whether the current thread can lock a mutex.
00371     struct _Can_lock
00372     {
00373       // Returns true if the mutex is unlocked or is locked by _M_caller.
00374       bool
00375       operator()() const noexcept
00376       { return _M_mx->_M_count == 0 || _M_mx->_M_owner == _M_caller; }
00377 
00378       const recursive_timed_mutex* _M_mx;
00379       thread::id _M_caller;
00380     };
00381 
00382   public:
00383 
00384     recursive_timed_mutex() = default;
00385     ~recursive_timed_mutex() { __glibcxx_assert( _M_count == 0 ); }
00386 
00387     recursive_timed_mutex(const recursive_timed_mutex&) = delete;
00388     recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
00389 
00390     void
00391     lock()
00392     {
00393       auto __id = this_thread::get_id();
00394       _Can_lock __can_lock{this, __id};
00395       unique_lock<mutex> __lk(_M_mut);
00396       _M_cv.wait(__lk, __can_lock);
00397       if (_M_count == -1u)
00398         __throw_system_error(EAGAIN); // [thread.timedmutex.recursive]/3
00399       _M_owner = __id;
00400       ++_M_count;
00401     }
00402 
00403     bool
00404     try_lock()
00405     {
00406       auto __id = this_thread::get_id();
00407       _Can_lock __can_lock{this, __id};
00408       lock_guard<mutex> __lk(_M_mut);
00409       if (!__can_lock())
00410         return false;
00411       if (_M_count == -1u)
00412         return false;
00413       _M_owner = __id;
00414       ++_M_count;
00415       return true;
00416     }
00417 
00418     template<typename _Rep, typename _Period>
00419       bool
00420       try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
00421       {
00422         auto __id = this_thread::get_id();
00423         _Can_lock __can_lock{this, __id};
00424         unique_lock<mutex> __lk(_M_mut);
00425         if (!_M_cv.wait_for(__lk, __rtime, __can_lock))
00426           return false;
00427         if (_M_count == -1u)
00428           return false;
00429         _M_owner = __id;
00430         ++_M_count;
00431         return true;
00432       }
00433 
00434     template<typename _Clock, typename _Duration>
00435       bool
00436       try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
00437       {
00438         auto __id = this_thread::get_id();
00439         _Can_lock __can_lock{this, __id};
00440         unique_lock<mutex> __lk(_M_mut);
00441         if (!_M_cv.wait_until(__lk, __atime, __can_lock))
00442           return false;
00443         if (_M_count == -1u)
00444           return false;
00445         _M_owner = __id;
00446         ++_M_count;
00447         return true;
00448       }
00449 
00450     void
00451     unlock()
00452     {
00453       lock_guard<mutex> __lk(_M_mut);
00454       __glibcxx_assert( _M_owner == this_thread::get_id() );
00455       __glibcxx_assert( _M_count > 0 );
00456       if (--_M_count == 0)
00457         {
00458           _M_owner = {};
00459           _M_cv.notify_one();
00460         }
00461     }
00462   };
00463 
00464 #endif
00465 #endif // _GLIBCXX_HAS_GTHREADS
00466 
00467   template<typename _Lock>
00468     inline unique_lock<_Lock>
00469     __try_to_lock(_Lock& __l)
00470     { return unique_lock<_Lock>{__l, try_to_lock}; }
00471 
00472   template<int _Idx, bool _Continue = true>
00473     struct __try_lock_impl
00474     {
00475       template<typename... _Lock>
00476         static void
00477         __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
00478         {
00479           __idx = _Idx;
00480           auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
00481           if (__lock.owns_lock())
00482             {
00483               constexpr bool __cont = _Idx + 2 < sizeof...(_Lock);
00484               using __try_locker = __try_lock_impl<_Idx + 1, __cont>;
00485               __try_locker::__do_try_lock(__locks, __idx);
00486               if (__idx == -1)
00487                 __lock.release();
00488             }
00489         }
00490     };
00491 
00492   template<int _Idx>
00493     struct __try_lock_impl<_Idx, false>
00494     {
00495       template<typename... _Lock>
00496         static void
00497         __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
00498         {
00499           __idx = _Idx;
00500           auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
00501           if (__lock.owns_lock())
00502             {
00503               __idx = -1;
00504               __lock.release();
00505             }
00506         }
00507     };
00508 
00509   /** @brief Generic try_lock.
00510    *  @param __l1 Meets Mutex requirements (try_lock() may throw).
00511    *  @param __l2 Meets Mutex requirements (try_lock() may throw).
00512    *  @param __l3 Meets Mutex requirements (try_lock() may throw).
00513    *  @return Returns -1 if all try_lock() calls return true. Otherwise returns
00514    *          a 0-based index corresponding to the argument that returned false.
00515    *  @post Either all arguments are locked, or none will be.
00516    *
00517    *  Sequentially calls try_lock() on each argument.
00518    */
00519   template<typename _Lock1, typename _Lock2, typename... _Lock3>
00520     int
00521     try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3)
00522     {
00523       int __idx;
00524       auto __locks = std::tie(__l1, __l2, __l3...);
00525       __try_lock_impl<0>::__do_try_lock(__locks, __idx);
00526       return __idx;
00527     }
00528 
00529   /** @brief Generic lock.
00530    *  @param __l1 Meets Mutex requirements (try_lock() may throw).
00531    *  @param __l2 Meets Mutex requirements (try_lock() may throw).
00532    *  @param __l3 Meets Mutex requirements (try_lock() may throw).
00533    *  @throw An exception thrown by an argument's lock() or try_lock() member.
00534    *  @post All arguments are locked.
00535    *
00536    *  All arguments are locked via a sequence of calls to lock(), try_lock()
00537    *  and unlock().  If the call exits via an exception any locks that were
00538    *  obtained will be released.
00539    */
00540   template<typename _L1, typename _L2, typename... _L3>
00541     void
00542     lock(_L1& __l1, _L2& __l2, _L3&... __l3)
00543     {
00544       while (true)
00545         {
00546           using __try_locker = __try_lock_impl<0, sizeof...(_L3) != 0>;
00547           unique_lock<_L1> __first(__l1);
00548           int __idx;
00549           auto __locks = std::tie(__l2, __l3...);
00550           __try_locker::__do_try_lock(__locks, __idx);
00551           if (__idx == -1)
00552             {
00553               __first.release();
00554               return;
00555             }
00556         }
00557     }
00558 
00559 #if __cplusplus >= 201703L
00560 #define __cpp_lib_scoped_lock 201703
00561   /** @brief A scoped lock type for multiple lockable objects.
00562    *
00563    * A scoped_lock controls mutex ownership within a scope, releasing
00564    * ownership in the destructor.
00565    */
00566   template<typename... _MutexTypes>
00567     class scoped_lock
00568     {
00569     public:
00570       explicit scoped_lock(_MutexTypes&... __m) : _M_devices(std::tie(__m...))
00571       { std::lock(__m...); }
00572 
00573       explicit scoped_lock(adopt_lock_t, _MutexTypes&... __m) noexcept
00574       : _M_devices(std::tie(__m...))
00575       { } // calling thread owns mutex
00576 
00577       ~scoped_lock()
00578       {
00579         std::apply([](_MutexTypes&... __m) {
00580           char __i[] __attribute__((__unused__)) = { (__m.unlock(), 0)... };
00581         }, _M_devices);
00582       }
00583 
00584       scoped_lock(const scoped_lock&) = delete;
00585       scoped_lock& operator=(const scoped_lock&) = delete;
00586 
00587     private:
00588       tuple<_MutexTypes&...> _M_devices;
00589     };
00590 
00591   template<>
00592     class scoped_lock<>
00593     {
00594     public:
00595       explicit scoped_lock() = default;
00596       explicit scoped_lock(adopt_lock_t) noexcept { }
00597       ~scoped_lock() = default;
00598 
00599       scoped_lock(const scoped_lock&) = delete;
00600       scoped_lock& operator=(const scoped_lock&) = delete;
00601     };
00602 
00603   template<typename _Mutex>
00604     class scoped_lock<_Mutex>
00605     {
00606     public:
00607       using mutex_type = _Mutex;
00608 
00609       explicit scoped_lock(mutex_type& __m) : _M_device(__m)
00610       { _M_device.lock(); }
00611 
00612       explicit scoped_lock(adopt_lock_t, mutex_type& __m) noexcept
00613       : _M_device(__m)
00614       { } // calling thread owns mutex
00615 
00616       ~scoped_lock()
00617       { _M_device.unlock(); }
00618 
00619       scoped_lock(const scoped_lock&) = delete;
00620       scoped_lock& operator=(const scoped_lock&) = delete;
00621 
00622     private:
00623       mutex_type&  _M_device;
00624     };
00625 #endif // C++17
00626 
00627 #ifdef _GLIBCXX_HAS_GTHREADS
00628   /// once_flag
00629   struct once_flag
00630   {
00631   private:
00632     typedef __gthread_once_t __native_type;
00633     __native_type  _M_once = __GTHREAD_ONCE_INIT;
00634 
00635   public:
00636     /// Constructor
00637     constexpr once_flag() noexcept = default;
00638 
00639     /// Deleted copy constructor
00640     once_flag(const once_flag&) = delete;
00641     /// Deleted assignment operator
00642     once_flag& operator=(const once_flag&) = delete;
00643 
00644     template<typename _Callable, typename... _Args>
00645       friend void
00646       call_once(once_flag& __once, _Callable&& __f, _Args&&... __args);
00647   };
00648 
00649 #ifdef _GLIBCXX_HAVE_TLS
00650   extern __thread void* __once_callable;
00651   extern __thread void (*__once_call)();
00652 #else
00653   extern function<void()> __once_functor;
00654 
00655   extern void
00656   __set_once_functor_lock_ptr(unique_lock<mutex>*);
00657 
00658   extern mutex&
00659   __get_once_mutex();
00660 #endif
00661 
00662   extern "C" void __once_proxy(void);
00663 
00664   /// call_once
00665   template<typename _Callable, typename... _Args>
00666     void
00667     call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
00668     {
00669       // _GLIBCXX_RESOLVE_LIB_DEFECTS
00670       // 2442. call_once() shouldn't DECAY_COPY()
00671       auto __callable = [&] {
00672           std::__invoke(std::forward<_Callable>(__f),
00673                         std::forward<_Args>(__args)...);
00674       };
00675 #ifdef _GLIBCXX_HAVE_TLS
00676       __once_callable = std::__addressof(__callable);
00677       __once_call = []{ (*(decltype(__callable)*)__once_callable)(); };
00678 #else
00679       unique_lock<mutex> __functor_lock(__get_once_mutex());
00680       __once_functor = __callable;
00681       __set_once_functor_lock_ptr(&__functor_lock);
00682 #endif
00683 
00684       int __e = __gthread_once(&__once._M_once, &__once_proxy);
00685 
00686 #ifndef _GLIBCXX_HAVE_TLS
00687       if (__functor_lock)
00688         __set_once_functor_lock_ptr(0);
00689 #endif
00690 
00691 #ifdef __clang_analyzer__
00692       // PR libstdc++/82481
00693       __once_callable = nullptr;
00694       __once_call = nullptr;
00695 #endif
00696 
00697       if (__e)
00698         __throw_system_error(__e);
00699     }
00700 #endif // _GLIBCXX_HAS_GTHREADS
00701 
00702   // @} group mutexes
00703 _GLIBCXX_END_NAMESPACE_VERSION
00704 } // namespace
00705 #endif // _GLIBCXX_USE_C99_STDINT_TR1
00706 
00707 #endif // C++11
00708 
00709 #endif // _GLIBCXX_MUTEX