shared_mutex (shared_timed_mutex): Add comments to explain the logic in the non-pthread_rwlock_t version.
* include/std/shared_mutex (shared_timed_mutex): Add comments to explain the logic in the non-pthread_rwlock_t version. (_Mutex): Remove redundant type. (_M_n_readers): Rename to _S_max_readers. (_M_write_entered, _M_readers): New convenience functions. (lock, lock_shared, try_lock_shared, unlock_shared): Use convenience functions. Use predicates with condition variables. Simplify bitwise operations. (try_lock_for, try_shared_lock_for): Convert duration to time_point and call try_lock_until or try_shared_lock_until respectively. (try_lock_until, try_shared_lock_until): Wait on the condition variables until the specified time passes. (unlock): Add Debug Mode assertion. (unlock_shared): Add Debug Mode assertion. * testsuite/30_threads/shared_timed_mutex/try_lock/3.cc: New. From-SVN: r221970
This commit is contained in:
parent
dccd8858cf
commit
43b0e124e4
|
@ -1,3 +1,21 @@
|
||||||
|
2015-04-10 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
|
* include/std/shared_mutex (shared_timed_mutex): Add comments to
|
||||||
|
explain the logic in the non-pthread_rwlock_t version.
|
||||||
|
(_Mutex): Remove redundant type.
|
||||||
|
(_M_n_readers): Rename to _S_max_readers.
|
||||||
|
(_M_write_entered, _M_readers): New convenience functions.
|
||||||
|
(lock, lock_shared, try_lock_shared, unlock_shared): Use convenience
|
||||||
|
functions. Use predicates with condition variables. Simplify bitwise
|
||||||
|
operations.
|
||||||
|
(try_lock_for, try_shared_lock_for): Convert duration to time_point
|
||||||
|
and call try_lock_until or try_shared_lock_until respectively.
|
||||||
|
(try_lock_until, try_shared_lock_until): Wait on the condition
|
||||||
|
variables until the specified time passes.
|
||||||
|
(unlock): Add Debug Mode assertion.
|
||||||
|
(unlock_shared): Add Debug Mode assertion.
|
||||||
|
* testsuite/30_threads/shared_timed_mutex/try_lock/3.cc: New.
|
||||||
|
|
||||||
2015-04-09 H.J. Lu <hongjiu.lu@intel.com>
|
2015-04-09 H.J. Lu <hongjiu.lu@intel.com>
|
||||||
|
|
||||||
* config/abi/post/x86_64-linux-gnu/x32/baseline_symbols.txt: Update.
|
* config/abi/post/x86_64-linux-gnu/x32/baseline_symbols.txt: Update.
|
||||||
|
|
|
@ -268,33 +268,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
|
|
||||||
#else // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
|
#else // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
|
||||||
|
|
||||||
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
|
// Must use the same clock as condition_variable
|
||||||
struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
|
typedef chrono::system_clock __clock_t;
|
||||||
{
|
|
||||||
template<typename _Rep, typename _Period>
|
|
||||||
bool
|
|
||||||
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
|
|
||||||
{ return _M_try_lock_for(__rtime); }
|
|
||||||
|
|
||||||
template<typename _Clock, typename _Duration>
|
// Based on Howard Hinnant's reference implementation from N2406.
|
||||||
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
|
// 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.
|
||||||
|
|
||||||
_Mutex _M_mut;
|
// 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;
|
condition_variable _M_gate1;
|
||||||
|
// Used to block queued writers while reader count is non-zero.
|
||||||
condition_variable _M_gate2;
|
condition_variable _M_gate2;
|
||||||
|
// The write-entered flag and reader count.
|
||||||
unsigned _M_state;
|
unsigned _M_state;
|
||||||
|
|
||||||
static constexpr unsigned _S_write_entered
|
static constexpr unsigned _S_write_entered
|
||||||
= 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
|
= 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:
|
public:
|
||||||
shared_timed_mutex() : _M_state(0) {}
|
shared_timed_mutex() : _M_state(0) {}
|
||||||
|
@ -313,11 +332,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
lock()
|
lock()
|
||||||
{
|
{
|
||||||
unique_lock<mutex> __lk(_M_mut);
|
unique_lock<mutex> __lk(_M_mut);
|
||||||
while (_M_state & _S_write_entered)
|
// Wait until we can set the write-entered flag.
|
||||||
_M_gate1.wait(__lk);
|
_M_gate1.wait(__lk, [=]{ return !_M_write_entered(); });
|
||||||
_M_state |= _S_write_entered;
|
_M_state |= _S_write_entered;
|
||||||
while (_M_state & _M_n_readers)
|
// Then wait until there are no more readers.
|
||||||
_M_gate2.wait(__lk);
|
_M_gate2.wait(__lk, [=]{ return _M_readers() == 0; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -332,41 +351,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
|
|
||||||
template<typename _Rep, typename _Period>
|
template<typename _Rep, typename _Period>
|
||||||
bool
|
bool
|
||||||
try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
||||||
{
|
{
|
||||||
unique_lock<_Mutex> __lk(_M_mut, __rel_time);
|
return try_lock_until(__clock_t::now() + __rel_time);
|
||||||
if (__lk.owns_lock() && _M_state == 0)
|
|
||||||
{
|
|
||||||
_M_state = _S_write_entered;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename _Clock, typename _Duration>
|
template<typename _Clock, typename _Duration>
|
||||||
bool
|
bool
|
||||||
try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
|
try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
|
||||||
{
|
{
|
||||||
unique_lock<_Mutex> __lk(_M_mut, __abs_time);
|
unique_lock<mutex> __lk(_M_mut);
|
||||||
if (__lk.owns_lock() && _M_state == 0)
|
if (!_M_gate1.wait_until(__lk, __abs_time,
|
||||||
|
[=]{ return !_M_write_entered(); }))
|
||||||
{
|
{
|
||||||
_M_state = _S_write_entered;
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
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
|
void
|
||||||
unlock()
|
unlock()
|
||||||
{
|
{
|
||||||
{
|
lock_guard<mutex> __lk(_M_mut);
|
||||||
lock_guard<_Mutex> __lk(_M_mut);
|
_GLIBCXX_DEBUG_ASSERT( _M_write_entered() );
|
||||||
_M_state = 0;
|
_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();
|
_M_gate1.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,51 +397,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
lock_shared()
|
lock_shared()
|
||||||
{
|
{
|
||||||
unique_lock<mutex> __lk(_M_mut);
|
unique_lock<mutex> __lk(_M_mut);
|
||||||
while ((_M_state & _S_write_entered)
|
_M_gate1.wait(__lk, [=]{ return _M_state < _S_max_readers; });
|
||||||
|| (_M_state & _M_n_readers) == _M_n_readers)
|
++_M_state;
|
||||||
{
|
|
||||||
_M_gate1.wait(__lk);
|
|
||||||
}
|
|
||||||
unsigned __num_readers = (_M_state & _M_n_readers) + 1;
|
|
||||||
_M_state &= ~_M_n_readers;
|
|
||||||
_M_state |= __num_readers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
try_lock_shared()
|
try_lock_shared()
|
||||||
{
|
{
|
||||||
unique_lock<_Mutex> __lk(_M_mut, try_to_lock);
|
unique_lock<mutex> __lk(_M_mut, try_to_lock);
|
||||||
unsigned __num_readers = _M_state & _M_n_readers;
|
if (!__lk.owns_lock())
|
||||||
if (__lk.owns_lock() && !(_M_state & _S_write_entered)
|
return false;
|
||||||
&& __num_readers != _M_n_readers)
|
if (_M_state < _S_max_readers)
|
||||||
{
|
{
|
||||||
++__num_readers;
|
++_M_state;
|
||||||
_M_state &= ~_M_n_readers;
|
|
||||||
_M_state |= __num_readers;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
|
|
||||||
template<typename _Rep, typename _Period>
|
template<typename _Rep, typename _Period>
|
||||||
bool
|
bool
|
||||||
try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
||||||
{
|
{
|
||||||
unique_lock<_Mutex> __lk(_M_mut, __rel_time);
|
return try_lock_shared_until(__clock_t::now() + __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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename _Clock, typename _Duration>
|
template <typename _Clock, typename _Duration>
|
||||||
|
@ -428,38 +427,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
try_lock_shared_until(const chrono::time_point<_Clock,
|
try_lock_shared_until(const chrono::time_point<_Clock,
|
||||||
_Duration>& __abs_time)
|
_Duration>& __abs_time)
|
||||||
{
|
{
|
||||||
unique_lock<_Mutex> __lk(_M_mut, __abs_time);
|
unique_lock<mutex> __lk(_M_mut);
|
||||||
if (__lk.owns_lock())
|
if (!_M_gate1.wait_until(__lk, __abs_time,
|
||||||
|
[=]{ return _M_state < _S_max_readers; }))
|
||||||
{
|
{
|
||||||
unsigned __num_readers = _M_state & _M_n_readers;
|
return false;
|
||||||
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;
|
++_M_state;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
void
|
||||||
unlock_shared()
|
unlock_shared()
|
||||||
{
|
{
|
||||||
lock_guard<_Mutex> __lk(_M_mut);
|
lock_guard<mutex> __lk(_M_mut);
|
||||||
unsigned __num_readers = (_M_state & _M_n_readers) - 1;
|
_GLIBCXX_DEBUG_ASSERT( _M_readers() > 0 );
|
||||||
_M_state &= ~_M_n_readers;
|
auto __prev = _M_state--;
|
||||||
_M_state |= __num_readers;
|
if (_M_write_entered())
|
||||||
if (_M_state & _S_write_entered)
|
|
||||||
{
|
{
|
||||||
if (__num_readers == 0)
|
// Wake the queued writer if there are no more readers.
|
||||||
|
if (_M_readers() == 0)
|
||||||
_M_gate2.notify_one();
|
_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
|
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();
|
_M_gate1.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* powerpc-ibm-aix* } }
|
||||||
|
// { dg-options " -std=gnu++14 -pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* powerpc-ibm-aix* } }
|
||||||
|
// { dg-options " -std=gnu++14 -pthreads" { target *-*-solaris* } }
|
||||||
|
// { dg-options " -std=gnu++14 " { target *-*-cygwin *-*-darwin* } }
|
||||||
|
// { dg-require-cstdint "" }
|
||||||
|
// { dg-require-gthreads "" }
|
||||||
|
|
||||||
|
// Copyright (C) 2013-2015 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.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this library; see the file COPYING3. If not see
|
||||||
|
// <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <system_error>
|
||||||
|
#include <testsuite_hooks.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
bool test __attribute__((unused)) = true;
|
||||||
|
typedef std::shared_timed_mutex mutex_type;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mutex_type m;
|
||||||
|
m.lock();
|
||||||
|
bool b;
|
||||||
|
|
||||||
|
std::thread t([&] {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto timeout = 100ms;
|
||||||
|
auto start = system_clock::now();
|
||||||
|
b = m.try_lock_for(timeout);
|
||||||
|
auto t = system_clock::now() - start;
|
||||||
|
VERIFY( !b );
|
||||||
|
VERIFY( t >= timeout );
|
||||||
|
|
||||||
|
start = system_clock::now();
|
||||||
|
b = m.try_lock_until(start + timeout);
|
||||||
|
t = system_clock::now() - start;
|
||||||
|
VERIFY( !b );
|
||||||
|
VERIFY( t >= timeout );
|
||||||
|
}
|
||||||
|
catch (const std::system_error& e)
|
||||||
|
{
|
||||||
|
VERIFY( false );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.join();
|
||||||
|
m.unlock();
|
||||||
|
}
|
||||||
|
catch (const std::system_error& e)
|
||||||
|
{
|
||||||
|
VERIFY( false );
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
VERIFY( false );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue