| // <mutex> -*- C++ -*- |
| |
| // Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
| // 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 mutex |
| * This is a Standard C++ Library header. |
| */ |
| |
| #ifndef _GLIBCXX_MUTEX |
| #define _GLIBCXX_MUTEX 1 |
| |
| #pragma GCC system_header |
| |
| #ifndef __GXX_EXPERIMENTAL_CXX0X__ |
| # include <c++0x_warning.h> |
| #else |
| |
| #include <tuple> |
| #include <cstddef> |
| #include <chrono> |
| #include <exception> |
| #include <type_traits> |
| #include <functional> |
| #include <system_error> |
| #include <bits/functexcept.h> |
| #include <bits/gthr.h> |
| #include <bits/move.h> // for std::swap |
| |
| #if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1) |
| |
| namespace std |
| { |
| /** |
| * @defgroup mutexes Mutexes |
| * @ingroup concurrency |
| * |
| * Classes for mutex support. |
| * @{ |
| */ |
| |
| /// mutex |
| class mutex |
| { |
| typedef __gthread_mutex_t __native_type; |
| __native_type _M_mutex; |
| |
| public: |
| typedef __native_type* native_handle_type; |
| |
| mutex() |
| { |
| // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may) |
| #ifdef __GTHREAD_MUTEX_INIT |
| __native_type __tmp = __GTHREAD_MUTEX_INIT; |
| _M_mutex = __tmp; |
| #else |
| __GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex); |
| #endif |
| } |
| |
| mutex(const mutex&) = delete; |
| mutex& operator=(const mutex&) = delete; |
| |
| void |
| lock() |
| { |
| int __e = __gthread_mutex_lock(&_M_mutex); |
| |
| // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may) |
| if (__e) |
| __throw_system_error(__e); |
| } |
| |
| bool |
| try_lock() |
| { |
| // XXX EINVAL, EAGAIN, EBUSY |
| return !__gthread_mutex_trylock(&_M_mutex); |
| } |
| |
| void |
| unlock() |
| { |
| // XXX EINVAL, EAGAIN, EPERM |
| __gthread_mutex_unlock(&_M_mutex); |
| } |
| |
| native_handle_type |
| native_handle() |
| { return &_M_mutex; } |
| }; |
| |
| /// recursive_mutex |
| class recursive_mutex |
| { |
| typedef __gthread_recursive_mutex_t __native_type; |
| __native_type _M_mutex; |
| |
| public: |
| typedef __native_type* native_handle_type; |
| |
| recursive_mutex() |
| { |
| // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may) |
| #ifdef __GTHREAD_RECURSIVE_MUTEX_INIT |
| __native_type __tmp = __GTHREAD_RECURSIVE_MUTEX_INIT; |
| _M_mutex = __tmp; |
| #else |
| __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex); |
| #endif |
| } |
| |
| 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() |
| { |
| // 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() |
| { return &_M_mutex; } |
| }; |
| |
| /// timed_mutex |
| class timed_mutex |
| { |
| typedef __gthread_mutex_t __native_type; |
| |
| #ifdef _GLIBCXX_USE_CLOCK_MONOTONIC |
| typedef chrono::monotonic_clock __clock_t; |
| #else |
| typedef chrono::high_resolution_clock __clock_t; |
| #endif |
| |
| __native_type _M_mutex; |
| |
| public: |
| typedef __native_type* native_handle_type; |
| |
| timed_mutex() |
| { |
| #ifdef __GTHREAD_MUTEX_INIT |
| __native_type __tmp = __GTHREAD_MUTEX_INIT; |
| _M_mutex = __tmp; |
| #else |
| __GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex); |
| #endif |
| } |
| |
| 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() |
| { |
| // 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 __try_lock_for_impl(__rtime); } |
| |
| template <class _Clock, class _Duration> |
| bool |
| try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) |
| { |
| chrono::time_point<_Clock, chrono::seconds> __s = |
| chrono::time_point_cast<chrono::seconds>(__atime); |
| |
| chrono::nanoseconds __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 !__gthread_mutex_timedlock(&_M_mutex, &__ts); |
| } |
| |
| void |
| unlock() |
| { |
| // XXX EINVAL, EAGAIN, EBUSY |
| __gthread_mutex_unlock(&_M_mutex); |
| } |
| |
| native_handle_type |
| native_handle() |
| { return &_M_mutex; } |
| |
| private: |
| template<typename _Rep, typename _Period> |
| typename enable_if< |
| ratio_less_equal<__clock_t::period, _Period>::value, bool>::type |
| __try_lock_for_impl(const chrono::duration<_Rep, _Period>& __rtime) |
| { |
| __clock_t::time_point __atime = __clock_t::now() |
| + chrono::duration_cast<__clock_t::duration>(__rtime); |
| |
| return try_lock_until(__atime); |
| } |
| |
| template <typename _Rep, typename _Period> |
| typename enable_if< |
| !ratio_less_equal<__clock_t::period, _Period>::value, bool>::type |
| __try_lock_for_impl(const chrono::duration<_Rep, _Period>& __rtime) |
| { |
| __clock_t::time_point __atime = __clock_t::now() |
| + ++chrono::duration_cast<__clock_t::duration>(__rtime); |
| |
| return try_lock_until(__atime); |
| } |
| }; |
| |
| /// recursive_timed_mutex |
| class recursive_timed_mutex |
| { |
| typedef __gthread_recursive_mutex_t __native_type; |
| |
| #ifdef _GLIBCXX_USE_CLOCK_MONOTONIC |
| typedef chrono::monotonic_clock __clock_t; |
| #else |
| typedef chrono::high_resolution_clock __clock_t; |
| #endif |
| |
| __native_type _M_mutex; |
| |
| public: |
| typedef __native_type* native_handle_type; |
| |
| recursive_timed_mutex() |
| { |
| // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may) |
| #ifdef __GTHREAD_RECURSIVE_MUTEX_INIT |
| __native_type __tmp = __GTHREAD_RECURSIVE_MUTEX_INIT; |
| _M_mutex = __tmp; |
| #else |
| __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex); |
| #endif |
| } |
| |
| 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() |
| { |
| // 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 __try_lock_for_impl(__rtime); } |
| |
| template <class _Clock, class _Duration> |
| bool |
| try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) |
| { |
| chrono::time_point<_Clock, chrono::seconds> __s = |
| chrono::time_point_cast<chrono::seconds>(__atime); |
| |
| chrono::nanoseconds __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 !__gthread_recursive_mutex_timedlock(&_M_mutex, &__ts); |
| } |
| |
| void |
| unlock() |
| { |
| // XXX EINVAL, EAGAIN, EBUSY |
| __gthread_recursive_mutex_unlock(&_M_mutex); |
| } |
| |
| native_handle_type |
| native_handle() |
| { return &_M_mutex; } |
| |
| private: |
| template<typename _Rep, typename _Period> |
| typename enable_if< |
| ratio_less_equal<__clock_t::period, _Period>::value, bool>::type |
| __try_lock_for_impl(const chrono::duration<_Rep, _Period>& __rtime) |
| { |
| __clock_t::time_point __atime = __clock_t::now() |
| + chrono::duration_cast<__clock_t::duration>(__rtime); |
| |
| return try_lock_until(__atime); |
| } |
| |
| template <typename _Rep, typename _Period> |
| typename enable_if< |
| !ratio_less_equal<__clock_t::period, _Period>::value, bool>::type |
| __try_lock_for_impl(const chrono::duration<_Rep, _Period>& __rtime) |
| { |
| __clock_t::time_point __atime = __clock_t::now() |
| + ++chrono::duration_cast<__clock_t::duration>(__rtime); |
| |
| return try_lock_until(__atime); |
| } |
| }; |
| |
| /// Do not acquire ownership of the mutex. |
| struct defer_lock_t { }; |
| |
| /// Try to acquire ownership of the mutex without blocking. |
| struct try_to_lock_t { }; |
| |
| /// Assume the calling thread has already obtained mutex ownership |
| /// and manage it. |
| struct adopt_lock_t { }; |
| |
| extern const defer_lock_t defer_lock; |
| extern const try_to_lock_t try_to_lock; |
| extern const adopt_lock_t adopt_lock; |
| |
| /** |
| * @brief Thrown to indicate errors with lock operations. |
| * |
| * @ingroup exceptions |
| */ |
| class lock_error : public exception |
| { |
| public: |
| virtual const char* |
| what() const throw(); |
| }; |
| |
| /// @brief Scoped lock idiom. |
| // Acquire the mutex here with a constructor call, then release with |
| // the destructor call in accordance with RAII style. |
| template<typename _Mutex> |
| class lock_guard |
| { |
| public: |
| typedef _Mutex mutex_type; |
| |
| explicit lock_guard(mutex_type& __m) : _M_device(__m) |
| { _M_device.lock(); } |
| |
| lock_guard(mutex_type& __m, adopt_lock_t __a) : _M_device(__m) |
| { _M_device.lock(); } |
| |
| ~lock_guard() |
| { _M_device.unlock(); } |
| |
| lock_guard(const lock_guard&) = delete; |
| lock_guard& operator=(const lock_guard&) = delete; |
| |
| private: |
| mutex_type& _M_device; |
| }; |
| |
| /// unique_lock |
| template<typename _Mutex> |
| class unique_lock |
| { |
| public: |
| typedef _Mutex mutex_type; |
| |
| unique_lock() |
| : _M_device(0), _M_owns(false) |
| { } |
| |
| explicit unique_lock(mutex_type& __m) |
| : _M_device(&__m), _M_owns(false) |
| { |
| lock(); |
| _M_owns = true; |
| } |
| |
| unique_lock(mutex_type& __m, defer_lock_t) |
| : _M_device(&__m), _M_owns(false) |
| { } |
| |
| unique_lock(mutex_type& __m, try_to_lock_t) |
| : _M_device(&__m), _M_owns(_M_device->try_lock()) |
| { } |
| |
| unique_lock(mutex_type& __m, adopt_lock_t) |
| : _M_device(&__m), _M_owns(true) |
| { |
| // XXX calling thread owns mutex |
| } |
| |
| template<typename _Clock, typename _Duration> |
| unique_lock(mutex_type& __m, |
| const chrono::time_point<_Clock, _Duration>& __atime) |
| : _M_device(&__m), _M_owns(_M_device->try_lock_until(__atime)) |
| { } |
| |
| template<typename _Rep, typename _Period> |
| unique_lock(mutex_type& __m, |
| const chrono::duration<_Rep, _Period>& __rtime) |
| : _M_device(&__m), _M_owns(_M_device->try_lock_for(__rtime)) |
| { } |
| |
| ~unique_lock() |
| { |
| if (_M_owns) |
| unlock(); |
| } |
| |
| unique_lock(const unique_lock&) = delete; |
| unique_lock& operator=(const unique_lock&) = delete; |
| |
| unique_lock(unique_lock&& __u) |
| : _M_device(__u._M_device), _M_owns(__u._M_owns) |
| { |
| __u._M_device = 0; |
| __u._M_owns = false; |
| } |
| |
| unique_lock& operator=(unique_lock&& __u) |
| { |
| if(_M_owns) |
| unlock(); |
| |
| unique_lock(std::move(__u)).swap(*this); |
| |
| __u._M_device = 0; |
| __u._M_owns = false; |
| |
| return *this; |
| } |
| |
| void |
| lock() |
| { |
| if (!_M_device) |
| __throw_system_error(int(errc::operation_not_permitted)); |
| else if (_M_owns) |
| __throw_system_error(int(errc::resource_deadlock_would_occur)); |
| else |
| { |
| _M_device->lock(); |
| _M_owns = true; |
| } |
| } |
| |
| bool |
| try_lock() |
| { |
| if (!_M_device) |
| __throw_system_error(int(errc::operation_not_permitted)); |
| else if (_M_owns) |
| __throw_system_error(int(errc::resource_deadlock_would_occur)); |
| else |
| { |
| _M_owns = _M_device->try_lock(); |
| return _M_owns; |
| } |
| } |
| |
| template<typename _Clock, typename _Duration> |
| bool |
| try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) |
| { |
| if (!_M_device) |
| __throw_system_error(int(errc::operation_not_permitted)); |
| else if (_M_owns) |
| __throw_system_error(int(errc::resource_deadlock_would_occur)); |
| else |
| { |
| _M_owns = _M_device->try_lock_until(__atime); |
| return _M_owns; |
| } |
| } |
| |
| template<typename _Rep, typename _Period> |
| bool |
| try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) |
| { |
| if (!_M_device) |
| __throw_system_error(int(errc::operation_not_permitted)); |
| else if (_M_owns) |
| __throw_system_error(int(errc::resource_deadlock_would_occur)); |
| else |
| { |
| _M_owns = _M_device->try_lock_for(__rtime); |
| return _M_owns; |
| } |
| } |
| |
| void |
| unlock() |
| { |
| if (!_M_owns) |
| __throw_system_error(int(errc::operation_not_permitted)); |
| else if (_M_device) |
| { |
| _M_device->unlock(); |
| _M_owns = false; |
| } |
| } |
| |
| void |
| swap(unique_lock&& __u) |
| { |
| std::swap(_M_device, __u._M_device); |
| std::swap(_M_owns, __u._M_owns); |
| } |
| |
| mutex_type* |
| release() |
| { |
| mutex_type* __ret = _M_device; |
| _M_device = 0; |
| _M_owns = false; |
| return __ret; |
| } |
| |
| bool |
| owns_lock() const |
| { return _M_owns; } |
| |
| /* explicit */ operator bool () const |
| { return owns_lock(); } |
| |
| mutex_type* |
| mutex() const |
| { return _M_device; } |
| |
| private: |
| mutex_type* _M_device; |
| bool _M_owns; // XXX use atomic_bool |
| }; |
| |
| template<typename _Mutex> |
| inline void |
| swap(unique_lock<_Mutex>& __x, unique_lock<_Mutex>& __y) |
| { __x.swap(__y); } |
| |
| template<typename _Mutex> |
| inline void |
| swap(unique_lock<_Mutex>&& __x, unique_lock<_Mutex>& __y) |
| { __x.swap(__y); } |
| |
| template<typename _Mutex> |
| inline void |
| swap(unique_lock<_Mutex>& __x, unique_lock<_Mutex>&& __y) |
| { __x.swap(__y); } |
| |
| template<int _Idx> |
| struct __unlock_impl |
| { |
| template<typename... _Lock> |
| static void |
| __do_unlock(tuple<_Lock&...>& __locks) |
| { |
| std::get<_Idx>(__locks).unlock(); |
| __unlock_impl<_Idx - 1>::__do_unlock(__locks); |
| } |
| }; |
| |
| template<> |
| struct __unlock_impl<-1> |
| { |
| template<typename... _Lock> |
| static void |
| __do_unlock(tuple<_Lock&...>&) |
| { } |
| }; |
| |
| template<int _Idx, bool _Continue = true> |
| struct __try_lock_impl |
| { |
| template<typename... _Lock> |
| static int |
| __do_try_lock(tuple<_Lock&...>& __locks) |
| { |
| if(std::get<_Idx>(__locks).try_lock()) |
| { |
| return __try_lock_impl<_Idx + 1, |
| _Idx + 2 < sizeof...(_Lock)>::__do_try_lock(__locks); |
| } |
| else |
| { |
| __unlock_impl<_Idx>::__do_unlock(__locks); |
| return _Idx; |
| } |
| } |
| }; |
| |
| template<int _Idx> |
| struct __try_lock_impl<_Idx, false> |
| { |
| template<typename... _Lock> |
| static int |
| __do_try_lock(tuple<_Lock&...>& __locks) |
| { |
| if(std::get<_Idx>(__locks).try_lock()) |
| return -1; |
| else |
| { |
| __unlock_impl<_Idx>::__do_unlock(__locks); |
| return _Idx; |
| } |
| } |
| }; |
| |
| /** @brief Generic try_lock. |
| * @param __l1 Meets Mutex requirements (try_lock() may throw). |
| * @param __l2 Meets Mutex requirements (try_lock() may throw). |
| * @param __l3 Meets Mutex 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) |
| { |
| tuple<_Lock1&, _Lock2&, _Lock3&...> __locks(__l1, __l2, __l3...); |
| return __try_lock_impl<0>::__do_try_lock(__locks); |
| } |
| |
| /// lock |
| template<typename _L1, typename _L2, typename ..._L3> |
| void |
| lock(_L1&, _L2&, _L3&...); |
| |
| /// once_flag |
| struct once_flag |
| { |
| private: |
| typedef __gthread_once_t __native_type; |
| __native_type _M_once; |
| |
| public: |
| once_flag() |
| { |
| __native_type __tmp = __GTHREAD_ONCE_INIT; |
| _M_once = __tmp; |
| } |
| |
| once_flag(const once_flag&) = delete; |
| once_flag& operator=(const once_flag&) = delete; |
| |
| template<typename _Callable, typename... _Args> |
| friend void |
| call_once(once_flag& __once, _Callable __f, _Args&&... __args); |
| }; |
| |
| #ifdef _GLIBCXX_HAVE_TLS |
| extern __thread void* __once_callable; |
| extern __thread void (*__once_call)(); |
| |
| template<typename _Callable> |
| inline void |
| __once_call_impl() |
| { |
| (*(_Callable*)__once_callable)(); |
| } |
| #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(); |
| |
| /// call_once |
| template<typename _Callable, typename... _Args> |
| void |
| call_once(once_flag& __once, _Callable __f, _Args&&... __args) |
| { |
| #ifdef _GLIBCXX_HAVE_TLS |
| auto __bound_functor = bind(__f, __args...); |
| __once_callable = &__bound_functor; |
| __once_call = &__once_call_impl<decltype(__bound_functor)>; |
| #else |
| unique_lock<mutex> __functor_lock(__get_once_mutex()); |
| __once_functor = bind(__f, __args...); |
| __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 |
| |
| if (__e) |
| __throw_system_error(__e); |
| } |
| |
| // @} group mutexes |
| } |
| |
| #endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1 |
| |
| #endif // __GXX_EXPERIMENTAL_CXX0X__ |
| |
| #endif // _GLIBCXX_MUTEX |