Skip to content

Commit

Permalink
[libc++] Fix thread annotations on shared_mutex and shared_timed_mutex
Browse files Browse the repository at this point in the history
Based on the comment in https://reviews.llvm.org/D54290#4418958, these
attributes need to be on the top-level functions in order to work
properly. Also, add tests.

Fixes http://llvm.org/PR57035.

Differential Revision: https://reviews.llvm.org/D154354
  • Loading branch information
ldionne committed Jul 6, 2023
1 parent 619c6c0 commit 5b666cf
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 26 deletions.
1 change: 1 addition & 0 deletions libcxx/.clang-format
Expand Up @@ -52,6 +52,7 @@ AttributeMacros: [
'_LIBCPP_STANDALONE_DEBUG',
'_LIBCPP_TEMPLATE_DATA_VIS',
'_LIBCPP_TEMPLATE_VIS',
'_LIBCPP_THREAD_SAFETY_ANNOTATION',
'_LIBCPP_USING_IF_EXISTS',
'_LIBCPP_WEAK',
]
Expand Down
70 changes: 44 additions & 26 deletions libcxx/include/shared_mutex
Expand Up @@ -153,8 +153,7 @@ _LIBCPP_PUSH_MACROS

_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_;
Expand All @@ -170,21 +169,22 @@ _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex")) __shared_mutex_base
__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:
Expand All @@ -195,21 +195,35 @@ public:
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:
Expand All @@ -220,28 +234,32 @@ public:
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 <class _Rep, class _Period>
_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 <class _Clock, class _Duration>
_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 <class _Rep, class _Period>
_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 <class _Clock, class _Duration>
_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 <class _Clock, class _Duration>
Expand Down
@@ -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

// <shared_mutex>
//
// class shared_mutex;
//
// void lock();
// bool try_lock();
// void unlock();
//
// void lock_shared();
// bool try_lock_shared();
// void unlock_shared();

#include <shared_mutex>

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();
}
}
}
@@ -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

// <shared_mutex>
//
// class shared_timed_mutex;
//
// void lock();
// bool try_lock();
// bool try_lock_for(const std::chrono::duration<Rep, Period>&);
// bool try_lock_until(const std::chrono::time_point<Clock, Duration>&);
// void unlock();
//
// void lock_shared();
// bool try_lock_shared();
// bool try_lock_shared_for(const std::chrono::duration<Rep, Period>&);
// bool try_lock_shared_until(const std::chrono::time_point<Clock, Duration>&);
// void unlock_shared();

#include <chrono>
#include <shared_mutex>

std::shared_timed_mutex m;
int data __attribute__((guarded_by(m))) = 0;
void read(int);

void f(std::chrono::time_point<std::chrono::steady_clock> 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();
}
}
}

0 comments on commit 5b666cf

Please sign in to comment.