Index: include/shared_mutex =================================================================== --- include/shared_mutex +++ include/shared_mutex @@ -19,6 +19,29 @@ 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 { public: @@ -118,7 +141,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD -class _LIBCPP_TYPE_VIS shared_timed_mutex +struct _LIBCPP_TYPE_VIS __shared_mutex_base { mutex __mut_; condition_variable __gate1_; @@ -127,7 +150,59 @@ static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1); 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: shared_timed_mutex(); _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default; @@ -170,30 +245,30 @@ shared_timed_mutex::try_lock_until( const chrono::time_point<_Clock, _Duration>& __abs_time) { - unique_lock __lk(__mut_); - if (__state_ & __write_entered_) + unique_lock __lk(__base.__mut_); + if (__base.__state_ & __base.__write_entered_) { while (true) { - cv_status __status = __gate1_.wait_until(__lk, __abs_time); - if ((__state_ & __write_entered_) == 0) + cv_status __status = __base.__gate1_.wait_until(__lk, __abs_time); + if ((__base.__state_ & __base.__write_entered_) == 0) break; if (__status == cv_status::timeout) return false; } } - __state_ |= __write_entered_; - if (__state_ & __n_readers_) + __base.__state_ |= __base.__write_entered_; + if (__base.__state_ & __base.__n_readers_) { while (true) { - cv_status __status = __gate2_.wait_until(__lk, __abs_time); - if ((__state_ & __n_readers_) == 0) + cv_status __status = __base.__gate2_.wait_until(__lk, __abs_time); + if ((__base.__state_ & __base.__n_readers_) == 0) break; if (__status == cv_status::timeout) { - __state_ &= ~__write_entered_; - __gate1_.notify_all(); + __base.__state_ &= ~__base.__write_entered_; + __base.__gate1_.notify_all(); return false; } } @@ -206,22 +281,22 @@ shared_timed_mutex::try_lock_shared_until( const chrono::time_point<_Clock, _Duration>& __abs_time) { - unique_lock __lk(__mut_); - if ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_) + unique_lock __lk(__base.__mut_); + if ((__base.__state_ & __base.__write_entered_) || (__base.__state_ & __base.__n_readers_) == __base.__n_readers_) { while (true) { - cv_status status = __gate1_.wait_until(__lk, __abs_time); - if ((__state_ & __write_entered_) == 0 && - (__state_ & __n_readers_) < __n_readers_) + cv_status status = __base.__gate1_.wait_until(__lk, __abs_time); + if ((__base.__state_ & __base.__write_entered_) == 0 && + (__base.__state_ & __base.__n_readers_) < __base.__n_readers_) break; if (status == cv_status::timeout) return false; } } - unsigned __num_readers = (__state_ & __n_readers_) + 1; - __state_ &= ~__n_readers_; - __state_ |= __num_readers; + unsigned __num_readers = (__base.__state_ & __base.__n_readers_) + 1; + __base.__state_ &= ~__base.__n_readers_; + __base.__state_ |= __num_readers; return true; } Index: src/shared_mutex.cpp =================================================================== --- src/shared_mutex.cpp +++ src/shared_mutex.cpp @@ -15,7 +15,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD -shared_timed_mutex::shared_timed_mutex() +// Shared Mutex Base +__shared_mutex_base::__shared_mutex_base() : __state_(0) { } @@ -23,7 +24,7 @@ // Exclusive ownership void -shared_timed_mutex::lock() +__shared_mutex_base::lock() { unique_lock lk(__mut_); while (__state_ & __write_entered_) @@ -34,7 +35,7 @@ } bool -shared_timed_mutex::try_lock() +__shared_mutex_base::try_lock() { unique_lock lk(__mut_); if (__state_ == 0) @@ -46,7 +47,7 @@ } void -shared_timed_mutex::unlock() +__shared_mutex_base::unlock() { lock_guard _(__mut_); __state_ = 0; @@ -56,7 +57,7 @@ // Shared ownership void -shared_timed_mutex::lock_shared() +__shared_mutex_base::lock_shared() { unique_lock lk(__mut_); while ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_) @@ -67,7 +68,7 @@ } bool -shared_timed_mutex::try_lock_shared() +__shared_mutex_base::try_lock_shared() { unique_lock lk(__mut_); unsigned num_readers = __state_ & __n_readers_; @@ -82,7 +83,7 @@ } void -shared_timed_mutex::unlock_shared() +__shared_mutex_base::unlock_shared() { lock_guard _(__mut_); unsigned num_readers = (__state_ & __n_readers_) - 1; @@ -101,6 +102,16 @@ } +// 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 #endif // !_LIBCPP_HAS_NO_THREADS Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/nothing_to_do.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/nothing_to_do.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/nothing_to_do.pass.cpp @@ -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() +{ +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.fail.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.fail.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/assign.fail.cpp @@ -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. +// +//===----------------------------------------------------------------------===// + +// + +// class shared_mutex; + +// shared_mutex& operator=(const shared_mutex&) = delete; + +#include + +#include "test_macros.h" + +int main() +{ +#if TEST_STD_VER > 14 + std::shared_mutex m0; + std::shared_mutex m1; + m1 = m0; +#else +# error +#endif +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.fail.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.fail.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/copy.fail.cpp @@ -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. +// +//===----------------------------------------------------------------------===// + +// + +// class shared_mutex; + +// shared_mutex(const shared_mutex&) = delete; + +#include + +#include "test_macros.h" + +int main() +{ +#if TEST_STD_VER > 14 + std::shared_mutex m0; + std::shared_mutex m1(m0); +#else +# error +#endif +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp @@ -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 + +// + +// class shared_mutex; + +// shared_mutex(); + +#include + +int main() +{ + std::shared_mutex m; +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock.pass.cpp @@ -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 + +// + +// class shared_mutex; + +// void lock(); + +#include +#include +#include +#include + + +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(); +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/lock_shared.pass.cpp @@ -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 + +// + +// class shared_mutex; + +// void lock_shared(); + +#include +#include +#include +#include +#include + +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 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(); +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock.pass.cpp @@ -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 + +// + +// class shared_mutex; + +// bool try_lock(); + +#include +#include +#include +#include + +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(); +} Index: test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp =================================================================== --- test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp +++ test/std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/try_lock_shared.pass.cpp @@ -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 + +// + +// class shared_mutex; + +// bool try_lock_shared(); + +#include +#include +#include +#include +#include + +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 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(); +}