diff --git a/libcxx/.clang-format b/libcxx/.clang-format --- a/libcxx/.clang-format +++ b/libcxx/.clang-format @@ -52,6 +52,7 @@ '_LIBCPP_STANDALONE_DEBUG', '_LIBCPP_TEMPLATE_DATA_VIS', '_LIBCPP_TEMPLATE_VIS', + '_LIBCPP_THREAD_SAFETY_ANNOTATION', '_LIBCPP_USING_IF_EXISTS', '_LIBCPP_WEAK', ] diff --git a/libcxx/include/shared_mutex b/libcxx/include/shared_mutex --- a/libcxx/include/shared_mutex +++ b/libcxx/include/shared_mutex @@ -153,8 +153,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD -struct _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_MUTEX -_LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex")) __shared_mutex_base { +struct _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_MUTEX __shared_mutex_base { mutex __mut_; condition_variable __gate1_; condition_variable __gate2_; @@ -170,21 +169,22 @@ __shared_mutex_base& operator=(const __shared_mutex_base&) = delete; // Exclusive ownership - void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); // blocking - bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true)); - void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()); + void lock(); // blocking + bool try_lock(); + void unlock(); // Shared ownership - void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_shared_capability()); // blocking - bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_shared_capability(true)); - void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_shared_capability()); + 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 >= 17 -class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_mutex { +class _LIBCPP_EXPORTED_FROM_ABI + _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION(__capability__("shared_mutex")) shared_mutex { __shared_mutex_base __base_; public: @@ -195,21 +195,35 @@ shared_mutex& operator=(const shared_mutex&) = delete; // Exclusive ownership - _LIBCPP_HIDE_FROM_ABI void lock() { return __base_.lock(); } - _LIBCPP_HIDE_FROM_ABI bool try_lock() { return __base_.try_lock(); } - _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); } + _LIBCPP_HIDE_FROM_ABI void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_capability__()) { + return __base_.lock(); + } + _LIBCPP_HIDE_FROM_ABI bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)) { + return __base_.try_lock(); + } + _LIBCPP_HIDE_FROM_ABI void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_capability__()) { + return __base_.unlock(); + } // Shared ownership - _LIBCPP_HIDE_FROM_ABI void lock_shared() { return __base_.lock_shared(); } - _LIBCPP_HIDE_FROM_ABI bool try_lock_shared() { return __base_.try_lock_shared(); } - _LIBCPP_HIDE_FROM_ABI void unlock_shared() { return __base_.unlock_shared(); } + _LIBCPP_HIDE_FROM_ABI void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_shared_capability__()) { + return __base_.lock_shared(); + } + _LIBCPP_HIDE_FROM_ABI bool try_lock_shared() + _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)) { + return __base_.try_lock_shared(); + } + _LIBCPP_HIDE_FROM_ABI void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_shared_capability__()) { + return __base_.unlock_shared(); + } // typedef __shared_mutex_base::native_handle_type native_handle_type; // _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return __base::unlock_shared(); } }; # endif -class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_timed_mutex { +class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION( + __capability__("shared_timed_mutex")) shared_timed_mutex { __shared_mutex_base __base_; public: @@ -220,28 +234,32 @@ shared_timed_mutex& operator=(const shared_timed_mutex&) = delete; // Exclusive ownership - void lock(); - bool try_lock(); + void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_capability__()); + bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)); template - _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) { + _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time) + _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)) { return try_lock_until(chrono::steady_clock::now() + __rel_time); } template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS bool - try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time); - void unlock(); + try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time) + _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_capability__(true)); + void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_capability__()); // Shared ownership - void lock_shared(); - bool try_lock_shared(); + void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__acquire_shared_capability__()); + bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)); template - _LIBCPP_HIDE_FROM_ABI bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) { + _LIBCPP_HIDE_FROM_ABI bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time) + _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)) { return try_lock_shared_until(chrono::steady_clock::now() + __rel_time); } template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS bool - try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time); - void unlock_shared(); + try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time) + _LIBCPP_THREAD_SAFETY_ANNOTATION(__try_acquire_shared_capability__(true)); + void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(__release_shared_capability__()); }; template diff --git a/libcxx/test/libcxx/thread/thread.shared_mutex/thread_safety.verify.cpp b/libcxx/test/libcxx/thread/thread.shared_mutex/thread_safety.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/thread/thread.shared_mutex/thread_safety.verify.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: no-threads +// UNSUPPORTED: availability-shared_mutex-missing +// REQUIRES: thread-safety +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +// On Windows Clang bugs out when both __declspec and __attribute__ are present, +// the processing goes awry preventing the definition of the types. +// XFAIL: msvc + +// +// +// class shared_mutex; +// +// void lock(); +// bool try_lock(); +// void unlock(); +// +// void lock_shared(); +// bool try_lock_shared(); +// void unlock_shared(); + +#include + +std::shared_mutex m; +int data __attribute__((guarded_by(m))) = 0; +void read(int); + +void f() { + // Exclusive locking + { + m.lock(); + ++data; // ok + m.unlock(); + } + { + if (m.try_lock()) { + ++data; // ok + m.unlock(); + } + } + + // Shared locking + { + m.lock_shared(); + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_mutex 'm' exclusively}} + m.unlock_shared(); + } + { + if (m.try_lock_shared()) { + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_mutex 'm' exclusively}} + m.unlock_shared(); + } + } +} diff --git a/libcxx/test/libcxx/thread/thread.shared_timed_mutex/thread_safety.verify.cpp b/libcxx/test/libcxx/thread/thread.shared_timed_mutex/thread_safety.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/thread/thread.shared_timed_mutex/thread_safety.verify.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 +// UNSUPPORTED: no-threads +// UNSUPPORTED: availability-shared_mutex-missing +// REQUIRES: thread-safety +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +// On Windows Clang bugs out when both __declspec and __attribute__ are present, +// the processing goes awry preventing the definition of the types. +// XFAIL: msvc + +// +// +// class shared_timed_mutex; +// +// void lock(); +// bool try_lock(); +// bool try_lock_for(const std::chrono::duration&); +// bool try_lock_until(const std::chrono::time_point&); +// void unlock(); +// +// void lock_shared(); +// bool try_lock_shared(); +// bool try_lock_shared_for(const std::chrono::duration&); +// bool try_lock_shared_until(const std::chrono::time_point&); +// void unlock_shared(); + +#include +#include + +std::shared_timed_mutex m; +int data __attribute__((guarded_by(m))) = 0; +void read(int); + +void f(std::chrono::time_point tp, std::chrono::milliseconds d) { + // Exclusive locking + { + m.lock(); + ++data; // ok + m.unlock(); + } + { + if (m.try_lock()) { + ++data; // ok + m.unlock(); + } + } + { + if (m.try_lock_for(d)) { + ++data; // ok + m.unlock(); + } + } + { + if (m.try_lock_until(tp)) { + ++data; // ok + m.unlock(); + } + } + + // Shared locking + { + m.lock_shared(); + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_timed_mutex 'm' exclusively}} + m.unlock_shared(); + } + { + if (m.try_lock_shared()) { + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_timed_mutex 'm' exclusively}} + m.unlock_shared(); + } + } + { + if (m.try_lock_shared_for(d)) { + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_timed_mutex 'm' exclusively}} + m.unlock_shared(); + } + } + { + if (m.try_lock_shared_until(tp)) { + read(data); // ok + ++data; // expected-error {{writing variable 'data' requires holding shared_timed_mutex 'm' exclusively}} + m.unlock_shared(); + } + } +}