diff options
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/atomic_base.h | 5 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/stl_iterator_base_funcs.h | 21 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/stl_list.h | 39 | ||||
-rw-r--r-- | libstdc++-v3/include/std/atomic | 14 | ||||
-rw-r--r-- | libstdc++-v3/include/std/shared_mutex | 190 |
5 files changed, 158 insertions, 111 deletions
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 8104c986b52..79769cf46d8 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -240,7 +240,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: typedef _ITp __int_type; - __int_type _M_i; + static constexpr int _S_alignment = + sizeof(_ITp) > alignof(_ITp) ? sizeof(_ITp) : alignof(_ITp); + + alignas(_S_alignment) __int_type _M_i; public: __atomic_base() noexcept = default; diff --git a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h index a146611b25f..6ee2ffbc5f2 100644 --- a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h +++ b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h @@ -66,6 +66,12 @@ namespace std _GLIBCXX_VISIBILITY(default) { +_GLIBCXX_BEGIN_NAMESPACE_CONTAINER + // Forward declaration for the overloads of __distance. + template <typename> struct _List_iterator; + template <typename> struct _List_const_iterator; +_GLIBCXX_END_NAMESPACE_CONTAINER + _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _InputIterator> @@ -96,6 +102,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __last - __first; } +#if _GLIBCXX_USE_CXX11_ABI + // Forward declaration because of the qualified call in distance. + template<typename _Tp> + ptrdiff_t + __distance(_GLIBCXX_STD_C::_List_iterator<_Tp>, + _GLIBCXX_STD_C::_List_iterator<_Tp>, + input_iterator_tag); + + template<typename _Tp> + ptrdiff_t + __distance(_GLIBCXX_STD_C::_List_const_iterator<_Tp>, + _GLIBCXX_STD_C::_List_const_iterator<_Tp>, + input_iterator_tag); +#endif + /** * @brief A generalization of pointer arithmetic. * @param __first An input iterator. diff --git a/libstdc++-v3/include/bits/stl_list.h b/libstdc++-v3/include/bits/stl_list.h index f8bfff18b19..3401e5b4d25 100644 --- a/libstdc++-v3/include/bits/stl_list.h +++ b/libstdc++-v3/include/bits/stl_list.h @@ -1868,6 +1868,45 @@ _GLIBCXX_END_NAMESPACE_CXX11 { __x.swap(__y); } _GLIBCXX_END_NAMESPACE_CONTAINER + +#if _GLIBCXX_USE_CXX11_ABI +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + // Detect when distance is used to compute the size of the whole list. + template<typename _Tp> + inline ptrdiff_t + __distance(_GLIBCXX_STD_C::_List_iterator<_Tp> __first, + _GLIBCXX_STD_C::_List_iterator<_Tp> __last, + input_iterator_tag __tag) + { + typedef _GLIBCXX_STD_C::_List_const_iterator<_Tp> _CIter; + return std::__distance(_CIter(__first), _CIter(__last), __tag); + } + + template<typename _Tp> + inline ptrdiff_t + __distance(_GLIBCXX_STD_C::_List_const_iterator<_Tp> __first, + _GLIBCXX_STD_C::_List_const_iterator<_Tp> __last, + input_iterator_tag) + { + typedef _GLIBCXX_STD_C::_List_node<size_t> _Sentinel; + _GLIBCXX_STD_C::_List_const_iterator<_Tp> __beyond = __last; + ++__beyond; + bool __whole = __first == __beyond; + if (__builtin_constant_p (__whole) && __whole) + return static_cast<const _Sentinel*>(__last._M_node)->_M_data; + + ptrdiff_t __n = 0; + while (__first != __last) + { + ++__first; + ++__n; + } + return __n; + } + +_GLIBCXX_END_NAMESPACE_VERSION +#endif } // namespace std #endif /* _STL_LIST_H */ diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 88c8b17dbc9..125e37a2838 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -165,18 +165,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct atomic { private: - // Align 1/2/4/8/16-byte types the same as integer types of that size. - // This matches the alignment effects of the C11 _Atomic qualifier. + // Align 1/2/4/8/16-byte types to at least their size. static constexpr int _S_min_alignment - = sizeof(_Tp) == sizeof(char) ? alignof(char) - : sizeof(_Tp) == sizeof(short) ? alignof(short) - : sizeof(_Tp) == sizeof(int) ? alignof(int) - : sizeof(_Tp) == sizeof(long) ? alignof(long) - : sizeof(_Tp) == sizeof(long long) ? alignof(long long) -#ifdef _GLIBCXX_USE_INT128 - : sizeof(_Tp) == sizeof(__int128) ? alignof(__int128) -#endif - : 0; + = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || sizeof(_Tp) > 16 + ? 0 : sizeof(_Tp); static constexpr int _S_alignment = _S_min_alignment > alignof(_Tp) ? _S_min_alignment : alignof(_Tp); diff --git a/libstdc++-v3/include/std/shared_mutex b/libstdc++-v3/include/std/shared_mutex index ab1b45b87ac..b72a822a57b 100644 --- a/libstdc++-v3/include/std/shared_mutex +++ b/libstdc++-v3/include/std/shared_mutex @@ -57,7 +57,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// shared_timed_mutex class shared_timed_mutex { -#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_T +#if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK typedef chrono::system_clock __clock_t; #ifdef PTHREAD_RWLOCK_INITIALIZER @@ -116,7 +116,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return true; } -#if _GTHREAD_USE_MUTEX_TIMEDLOCK template<typename _Rep, typename _Period> bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) @@ -158,7 +157,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const auto __s_atime = __s_entry + __delta; return try_lock_until(__s_atime); } -#endif void unlock() @@ -200,7 +198,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return true; } -#if _GTHREAD_USE_MUTEX_TIMEDLOCK template<typename _Rep, typename _Period> bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) @@ -258,7 +255,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const auto __s_atime = __s_entry + __delta; return try_lock_shared_until(__s_atime); } -#endif void unlock_shared() @@ -266,35 +262,54 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION unlock(); } -#else // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T - -#if _GTHREAD_USE_MUTEX_TIMEDLOCK - struct _Mutex : mutex, __timed_mutex_impl<_Mutex> - { - template<typename _Rep, typename _Period> - bool - try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) - { return _M_try_lock_for(__rtime); } +#else // ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK) - template<typename _Clock, typename _Duration> - bool - try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) - { return _M_try_lock_until(__atime); } - }; -#else - typedef mutex _Mutex; -#endif - - // Based on Howard Hinnant's reference implementation from N2406 + // Must use the same clock as condition_variable + typedef chrono::system_clock __clock_t; - _Mutex _M_mut; + // Based on Howard Hinnant's reference implementation from N2406. + + // The high bit of _M_state is the write-entered flag which is set to + // indicate a writer has taken the lock or is queuing to take the lock. + // The remaining bits are the count of reader locks. + // + // To take a reader lock, block on gate1 while the write-entered flag is + // set or the maximum number of reader locks is held, then increment the + // reader lock count. + // To release, decrement the count, then if the write-entered flag is set + // and the count is zero then signal gate2 to wake a queued writer, + // otherwise if the maximum number of reader locks was held signal gate1 + // to wake a reader. + // + // To take a writer lock, block on gate1 while the write-entered flag is + // set, then set the write-entered flag to start queueing, then block on + // gate2 while the number of reader locks is non-zero. + // To release, unset the write-entered flag and signal gate1 to wake all + // blocked readers and writers. + // + // This means that when no reader locks are held readers and writers get + // equal priority. When one or more reader locks is held a writer gets + // priority and no more reader locks can be taken while the writer is + // queued. + + // Only locked when accessing _M_state or waiting on condition variables. + mutex _M_mut; + // Used to block while write-entered is set or reader count at maximum. condition_variable _M_gate1; + // Used to block queued writers while reader count is non-zero. condition_variable _M_gate2; + // The write-entered flag and reader count. unsigned _M_state; static constexpr unsigned _S_write_entered = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1); - static constexpr unsigned _M_n_readers = ~_S_write_entered; + static constexpr unsigned _S_max_readers = ~_S_write_entered; + + // Test whether the write-entered flag is set. _M_mut must be locked. + bool _M_write_entered() const { return _M_state & _S_write_entered; } + + // The number of reader locks currently held. _M_mut must be locked. + unsigned _M_readers() const { return _M_state & _S_max_readers; } public: shared_timed_mutex() : _M_state(0) {} @@ -313,11 +328,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION lock() { unique_lock<mutex> __lk(_M_mut); - while (_M_state & _S_write_entered) - _M_gate1.wait(__lk); + // Wait until we can set the write-entered flag. + _M_gate1.wait(__lk, [=]{ return !_M_write_entered(); }); _M_state |= _S_write_entered; - while (_M_state & _M_n_readers) - _M_gate2.wait(__lk); + // Then wait until there are no more readers. + _M_gate2.wait(__lk, [=]{ return _M_readers() == 0; }); } bool @@ -332,41 +347,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return false; } -#if _GTHREAD_USE_MUTEX_TIMEDLOCK template<typename _Rep, typename _Period> bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) { - unique_lock<_Mutex> __lk(_M_mut, __rel_time); - if (__lk.owns_lock() && _M_state == 0) - { - _M_state = _S_write_entered; - return true; - } - return false; + return try_lock_until(__clock_t::now() + __rel_time); } template<typename _Clock, typename _Duration> bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time) { - unique_lock<_Mutex> __lk(_M_mut, __abs_time); - if (__lk.owns_lock() && _M_state == 0) + unique_lock<mutex> __lk(_M_mut); + if (!_M_gate1.wait_until(__lk, __abs_time, + [=]{ return !_M_write_entered(); })) { - _M_state = _S_write_entered; - return true; + return false; } - return false; + _M_state |= _S_write_entered; + if (!_M_gate2.wait_until(__lk, __abs_time, + [=]{ return _M_readers() == 0; })) + { + _M_state ^= _S_write_entered; + // Wake all threads blocked while the write-entered flag was set. + _M_gate1.notify_all(); + return false; + } + return true; } -#endif void unlock() { - { - lock_guard<_Mutex> __lk(_M_mut); - _M_state = 0; - } + lock_guard<mutex> __lk(_M_mut); + _GLIBCXX_DEBUG_ASSERT( _M_write_entered() ); + _M_state = 0; + // call notify_all() while mutex is held so that another thread can't + // lock and unlock the mutex then destroy *this before we make the call. _M_gate1.notify_all(); } @@ -376,51 +393,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION lock_shared() { unique_lock<mutex> __lk(_M_mut); - while ((_M_state & _S_write_entered) - || (_M_state & _M_n_readers) == _M_n_readers) - { - _M_gate1.wait(__lk); - } - unsigned __num_readers = (_M_state & _M_n_readers) + 1; - _M_state &= ~_M_n_readers; - _M_state |= __num_readers; + _M_gate1.wait(__lk, [=]{ return _M_state < _S_max_readers; }); + ++_M_state; } bool try_lock_shared() { - unique_lock<_Mutex> __lk(_M_mut, try_to_lock); - unsigned __num_readers = _M_state & _M_n_readers; - if (__lk.owns_lock() && !(_M_state & _S_write_entered) - && __num_readers != _M_n_readers) + unique_lock<mutex> __lk(_M_mut, try_to_lock); + if (!__lk.owns_lock()) + return false; + if (_M_state < _S_max_readers) { - ++__num_readers; - _M_state &= ~_M_n_readers; - _M_state |= __num_readers; + ++_M_state; return true; } return false; } -#if _GTHREAD_USE_MUTEX_TIMEDLOCK template<typename _Rep, typename _Period> bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) { - unique_lock<_Mutex> __lk(_M_mut, __rel_time); - if (__lk.owns_lock()) - { - unsigned __num_readers = _M_state & _M_n_readers; - if (!(_M_state & _S_write_entered) - && __num_readers != _M_n_readers) - { - ++__num_readers; - _M_state &= ~_M_n_readers; - _M_state |= __num_readers; - return true; - } - } - return false; + return try_lock_shared_until(__clock_t::now() + __rel_time); } template <typename _Clock, typename _Duration> @@ -428,42 +423,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time) { - unique_lock<_Mutex> __lk(_M_mut, __abs_time); - if (__lk.owns_lock()) + unique_lock<mutex> __lk(_M_mut); + if (!_M_gate1.wait_until(__lk, __abs_time, + [=]{ return _M_state < _S_max_readers; })) { - unsigned __num_readers = _M_state & _M_n_readers; - if (!(_M_state & _S_write_entered) - && __num_readers != _M_n_readers) - { - ++__num_readers; - _M_state &= ~_M_n_readers; - _M_state |= __num_readers; - return true; - } + return false; } - return false; + ++_M_state; + return true; } -#endif void unlock_shared() { - lock_guard<_Mutex> __lk(_M_mut); - unsigned __num_readers = (_M_state & _M_n_readers) - 1; - _M_state &= ~_M_n_readers; - _M_state |= __num_readers; - if (_M_state & _S_write_entered) + lock_guard<mutex> __lk(_M_mut); + _GLIBCXX_DEBUG_ASSERT( _M_readers() > 0 ); + auto __prev = _M_state--; + if (_M_write_entered()) { - if (__num_readers == 0) + // Wake the queued writer if there are no more readers. + if (_M_readers() == 0) _M_gate2.notify_one(); + // No need to notify gate1 because we give priority to the queued + // writer, and that writer will eventually notify gate1 after it + // clears the write-entered flag. } else { - if (__num_readers == _M_n_readers - 1) + // Wake any thread that was blocked on reader overflow. + if (__prev == _S_max_readers) _M_gate1.notify_one(); } } -#endif // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T +#endif // _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK }; #endif // _GLIBCXX_HAS_GTHREADS |