Implement N4508: shared_mutex. Reviewed as http://reviews.llvm.org/D10480

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@241067 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Marshall Clow 2015-06-30 14:04:14 +00:00
parent f3c8fb22ec
commit abadb458d0
10 changed files with 439 additions and 26 deletions

View File

@ -19,6 +19,29 @@
namespace std namespace std
{ {
class shared_mutex // C++17
{
public:
shared_mutex();
~shared_mutex();
shared_mutex(const shared_mutex&) = delete;
shared_mutex& operator=(const shared_mutex&) = delete;
// Exclusive ownership
void lock(); // blocking
bool try_lock();
void unlock();
// Shared ownership
void lock_shared(); // blocking
bool try_lock_shared();
void unlock_shared();
typedef implementation-defined native_handle_type; // See 30.2.3
native_handle_type native_handle(); // See 30.2.3
};
class shared_timed_mutex class shared_timed_mutex
{ {
public: public:
@ -118,7 +141,7 @@ template <class Mutex>
_LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD
class _LIBCPP_TYPE_VIS shared_timed_mutex struct _LIBCPP_TYPE_VIS __shared_mutex_base
{ {
mutex __mut_; mutex __mut_;
condition_variable __gate1_; condition_variable __gate1_;
@ -127,6 +150,58 @@ class _LIBCPP_TYPE_VIS shared_timed_mutex
static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1); static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
static const unsigned __n_readers_ = ~__write_entered_; static const unsigned __n_readers_ = ~__write_entered_;
__shared_mutex_base();
_LIBCPP_INLINE_VISIBILITY ~__shared_mutex_base() = default;
__shared_mutex_base(const __shared_mutex_base&) = delete;
__shared_mutex_base& operator=(const __shared_mutex_base&) = delete;
// Exclusive ownership
void lock(); // blocking
bool try_lock();
void unlock();
// Shared ownership
void lock_shared(); // blocking
bool try_lock_shared();
void unlock_shared();
// typedef implementation-defined native_handle_type; // See 30.2.3
// native_handle_type native_handle(); // See 30.2.3
};
#if _LIBCPP_STD_VER > 14
class _LIBCPP_TYPE_VIS shared_mutex
{
__shared_mutex_base __base;
public:
shared_mutex() : __base() {}
_LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default;
shared_mutex(const shared_mutex&) = delete;
shared_mutex& operator=(const shared_mutex&) = delete;
// Exclusive ownership
_LIBCPP_INLINE_VISIBILITY void lock() { return __base.lock(); }
_LIBCPP_INLINE_VISIBILITY bool try_lock() { return __base.try_lock(); }
_LIBCPP_INLINE_VISIBILITY void unlock() { return __base.unlock(); }
// Shared ownership
_LIBCPP_INLINE_VISIBILITY void lock_shared() { return __base.lock_shared(); }
_LIBCPP_INLINE_VISIBILITY bool try_lock_shared() { return __base.try_lock_shared(); }
_LIBCPP_INLINE_VISIBILITY void unlock_shared() { return __base.unlock_shared(); }
// typedef __shared_mutex_base::native_handle_type native_handle_type;
// _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() { return __base::unlock_shared(); }
};
#endif
class _LIBCPP_TYPE_VIS shared_timed_mutex
{
__shared_mutex_base __base;
public: public:
shared_timed_mutex(); shared_timed_mutex();
_LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default; _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default;
@ -170,30 +245,30 @@ bool
shared_timed_mutex::try_lock_until( shared_timed_mutex::try_lock_until(
const chrono::time_point<_Clock, _Duration>& __abs_time) const chrono::time_point<_Clock, _Duration>& __abs_time)
{ {
unique_lock<mutex> __lk(__mut_); unique_lock<mutex> __lk(__base.__mut_);
if (__state_ & __write_entered_) if (__base.__state_ & __base.__write_entered_)
{ {
while (true) while (true)
{ {
cv_status __status = __gate1_.wait_until(__lk, __abs_time); cv_status __status = __base.__gate1_.wait_until(__lk, __abs_time);
if ((__state_ & __write_entered_) == 0) if ((__base.__state_ & __base.__write_entered_) == 0)
break; break;
if (__status == cv_status::timeout) if (__status == cv_status::timeout)
return false; return false;
} }
} }
__state_ |= __write_entered_; __base.__state_ |= __base.__write_entered_;
if (__state_ & __n_readers_) if (__base.__state_ & __base.__n_readers_)
{ {
while (true) while (true)
{ {
cv_status __status = __gate2_.wait_until(__lk, __abs_time); cv_status __status = __base.__gate2_.wait_until(__lk, __abs_time);
if ((__state_ & __n_readers_) == 0) if ((__base.__state_ & __base.__n_readers_) == 0)
break; break;
if (__status == cv_status::timeout) if (__status == cv_status::timeout)
{ {
__state_ &= ~__write_entered_; __base.__state_ &= ~__base.__write_entered_;
__gate1_.notify_all(); __base.__gate1_.notify_all();
return false; return false;
} }
} }
@ -206,22 +281,22 @@ bool
shared_timed_mutex::try_lock_shared_until( shared_timed_mutex::try_lock_shared_until(
const chrono::time_point<_Clock, _Duration>& __abs_time) const chrono::time_point<_Clock, _Duration>& __abs_time)
{ {
unique_lock<mutex> __lk(__mut_); unique_lock<mutex> __lk(__base.__mut_);
if ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_) if ((__base.__state_ & __base.__write_entered_) || (__base.__state_ & __base.__n_readers_) == __base.__n_readers_)
{ {
while (true) while (true)
{ {
cv_status status = __gate1_.wait_until(__lk, __abs_time); cv_status status = __base.__gate1_.wait_until(__lk, __abs_time);
if ((__state_ & __write_entered_) == 0 && if ((__base.__state_ & __base.__write_entered_) == 0 &&
(__state_ & __n_readers_) < __n_readers_) (__base.__state_ & __base.__n_readers_) < __base.__n_readers_)
break; break;
if (status == cv_status::timeout) if (status == cv_status::timeout)
return false; return false;
} }
} }
unsigned __num_readers = (__state_ & __n_readers_) + 1; unsigned __num_readers = (__base.__state_ & __base.__n_readers_) + 1;
__state_ &= ~__n_readers_; __base.__state_ &= ~__base.__n_readers_;
__state_ |= __num_readers; __base.__state_ |= __num_readers;
return true; return true;
} }

View File

@ -15,7 +15,8 @@
_LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD
shared_timed_mutex::shared_timed_mutex() // Shared Mutex Base
__shared_mutex_base::__shared_mutex_base()
: __state_(0) : __state_(0)
{ {
} }
@ -23,7 +24,7 @@ shared_timed_mutex::shared_timed_mutex()
// Exclusive ownership // Exclusive ownership
void void
shared_timed_mutex::lock() __shared_mutex_base::lock()
{ {
unique_lock<mutex> lk(__mut_); unique_lock<mutex> lk(__mut_);
while (__state_ & __write_entered_) while (__state_ & __write_entered_)
@ -34,7 +35,7 @@ shared_timed_mutex::lock()
} }
bool bool
shared_timed_mutex::try_lock() __shared_mutex_base::try_lock()
{ {
unique_lock<mutex> lk(__mut_); unique_lock<mutex> lk(__mut_);
if (__state_ == 0) if (__state_ == 0)
@ -46,7 +47,7 @@ shared_timed_mutex::try_lock()
} }
void void
shared_timed_mutex::unlock() __shared_mutex_base::unlock()
{ {
lock_guard<mutex> _(__mut_); lock_guard<mutex> _(__mut_);
__state_ = 0; __state_ = 0;
@ -56,7 +57,7 @@ shared_timed_mutex::unlock()
// Shared ownership // Shared ownership
void void
shared_timed_mutex::lock_shared() __shared_mutex_base::lock_shared()
{ {
unique_lock<mutex> lk(__mut_); unique_lock<mutex> lk(__mut_);
while ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_) while ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_)
@ -67,7 +68,7 @@ shared_timed_mutex::lock_shared()
} }
bool bool
shared_timed_mutex::try_lock_shared() __shared_mutex_base::try_lock_shared()
{ {
unique_lock<mutex> lk(__mut_); unique_lock<mutex> lk(__mut_);
unsigned num_readers = __state_ & __n_readers_; unsigned num_readers = __state_ & __n_readers_;
@ -82,7 +83,7 @@ shared_timed_mutex::try_lock_shared()
} }
void void
shared_timed_mutex::unlock_shared() __shared_mutex_base::unlock_shared()
{ {
lock_guard<mutex> _(__mut_); lock_guard<mutex> _(__mut_);
unsigned num_readers = (__state_ & __n_readers_) - 1; unsigned num_readers = (__state_ & __n_readers_) - 1;
@ -101,6 +102,16 @@ shared_timed_mutex::unlock_shared()
} }
// Shared Timed Mutex
// These routines are here for ABI stability
shared_timed_mutex::shared_timed_mutex() : __base() {}
void shared_timed_mutex::lock() { return __base.lock(); }
bool shared_timed_mutex::try_lock() { return __base.try_lock(); }
void shared_timed_mutex::unlock() { return __base.unlock(); }
void shared_timed_mutex::lock_shared() { return __base.lock_shared(); }
bool shared_timed_mutex::try_lock_shared() { return __base.try_lock_shared(); }
void shared_timed_mutex::unlock_shared() { return __base.unlock_shared(); }
_LIBCPP_END_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
#endif // !_LIBCPP_HAS_NO_THREADS #endif // !_LIBCPP_HAS_NO_THREADS

View File

@ -0,0 +1,12 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
int main()
{
}

View File

@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// <shared_mutex>
// class shared_mutex;
// shared_mutex& operator=(const shared_mutex&) = delete;
#include <shared_mutex>
#include "test_macros.h"
int main()
{
#if TEST_STD_VER > 14
std::shared_mutex m0;
std::shared_mutex m1;
m1 = m0;
#else
# error
#endif
}

View File

@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// <shared_mutex>
// class shared_mutex;
// shared_mutex(const shared_mutex&) = delete;
#include <shared_mutex>
#include "test_macros.h"
int main()
{
#if TEST_STD_VER > 14
std::shared_mutex m0;
std::shared_mutex m1(m0);
#else
# error
#endif
}

View File

@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: libcpp-has-no-threads
// UNSUPPORTED: c++03, c++98, c++11, c++14
// <shared_mutex>
// class shared_mutex;
// shared_mutex();
#include <shared_mutex>
int main()
{
std::shared_mutex m;
}

View File

@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: libcpp-has-no-threads
// UNSUPPORTED: c++03, c++98, c++11, c++14
// <shared_mutex>
// class shared_mutex;
// void lock();
#include <shared_mutex>
#include <thread>
#include <cstdlib>
#include <cassert>
std::shared_mutex m;
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::duration duration;
typedef std::chrono::milliseconds ms;
typedef std::chrono::nanoseconds ns;
void f()
{
time_point t0 = Clock::now();
m.lock();
time_point t1 = Clock::now();
m.unlock();
ns d = t1 - t0 - ms(250);
assert(d < ms(50)); // within 50ms
}
int main()
{
m.lock();
std::thread t(f);
std::this_thread::sleep_for(ms(250));
m.unlock();
t.join();
}

View File

@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: libcpp-has-no-threads
// UNSUPPORTED: c++03, c++98, c++11, c++14
// <shared_mutex>
// class shared_mutex;
// void lock_shared();
#include <shared_mutex>
#include <thread>
#include <vector>
#include <cstdlib>
#include <cassert>
std::shared_mutex m;
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::duration duration;
typedef std::chrono::milliseconds ms;
typedef std::chrono::nanoseconds ns;
void f()
{
time_point t0 = Clock::now();
m.lock_shared();
time_point t1 = Clock::now();
m.unlock_shared();
ns d = t1 - t0 - ms(250);
assert(d < ms(50)); // within 50ms
}
void g()
{
time_point t0 = Clock::now();
m.lock_shared();
time_point t1 = Clock::now();
m.unlock_shared();
ns d = t1 - t0;
assert(d < ms(50)); // within 50ms
}
int main()
{
m.lock();
std::vector<std::thread> v;
for (int i = 0; i < 5; ++i)
v.push_back(std::thread(f));
std::this_thread::sleep_for(ms(250));
m.unlock();
for (auto& t : v)
t.join();
m.lock_shared();
for (auto& t : v)
t = std::thread(g);
std::thread q(f);
std::this_thread::sleep_for(ms(250));
m.unlock_shared();
for (auto& t : v)
t.join();
q.join();
}

View File

@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: libcpp-has-no-threads
// UNSUPPORTED: c++03, c++98, c++11, c++14
// <shared_mutex>
// class shared_mutex;
// bool try_lock();
#include <shared_mutex>
#include <thread>
#include <cstdlib>
#include <cassert>
std::shared_mutex m;
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::duration duration;
typedef std::chrono::milliseconds ms;
typedef std::chrono::nanoseconds ns;
void f()
{
time_point t0 = Clock::now();
assert(!m.try_lock());
assert(!m.try_lock());
assert(!m.try_lock());
while(!m.try_lock())
;
time_point t1 = Clock::now();
m.unlock();
ns d = t1 - t0 - ms(250);
assert(d < ms(200)); // within 200ms
}
int main()
{
m.lock();
std::thread t(f);
std::this_thread::sleep_for(ms(250));
m.unlock();
t.join();
}

View File

@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: libcpp-has-no-threads
// UNSUPPORTED: c++03, c++98, c++11, c++14
// <shared_mutex>
// class shared_mutex;
// bool try_lock_shared();
#include <shared_mutex>
#include <thread>
#include <vector>
#include <cstdlib>
#include <cassert>
std::shared_mutex m;
typedef std::chrono::system_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::duration duration;
typedef std::chrono::milliseconds ms;
typedef std::chrono::nanoseconds ns;
void f()
{
time_point t0 = Clock::now();
assert(!m.try_lock_shared());
assert(!m.try_lock_shared());
assert(!m.try_lock_shared());
while(!m.try_lock_shared())
;
time_point t1 = Clock::now();
m.unlock_shared();
ns d = t1 - t0 - ms(250);
assert(d < ms(200)); // within 200ms
}
int main()
{
m.lock();
std::vector<std::thread> v;
for (int i = 0; i < 5; ++i)
v.push_back(std::thread(f));
std::this_thread::sleep_for(ms(250));
m.unlock();
for (auto& t : v)
t.join();
}