Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -859,6 +859,11 @@ #endif #endif +#if (defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) && defined(__clang__) \ + && __has_attribute(acquire_capability)) +#define _LIBCPP_HAS_THREAD_SAFETY_ANNOTATIONS +#endif + #endif // __cplusplus #endif // _LIBCPP_CONFIG Index: include/__mutex_base =================================================================== --- include/__mutex_base +++ include/__mutex_base @@ -26,7 +26,15 @@ #ifndef _LIBCPP_HAS_NO_THREADS -class _LIBCPP_TYPE_VIS mutex +#ifndef _LIBCPP_THREAD_SAFETY_ANNOTATION +# ifdef _LIBCPP_HAS_THREAD_SAFETY_ANNOTATIONS +# define _LIBCPP_THREAD_SAFETY_ANNOTATION(x) __attribute__((x)) +# else +# define _LIBCPP_THREAD_SAFETY_ANNOTATION(x) +# endif +#endif // _LIBCPP_THREAD_SAFETY_ANNOTATION + +class _LIBCPP_TYPE_VIS _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("mutex")) mutex { pthread_mutex_t __m_; @@ -44,9 +52,9 @@ mutex& operator=(const mutex&);// = delete; public: - void lock(); - bool try_lock() _NOEXCEPT; - void unlock() _NOEXCEPT; + void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); + bool try_lock() _NOEXCEPT _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true)); + void unlock() _NOEXCEPT _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()); typedef pthread_mutex_t* native_handle_type; _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__m_;} @@ -71,7 +79,7 @@ #endif template -class _LIBCPP_TYPE_VIS_ONLY lock_guard +class _LIBCPP_TYPE_VIS_ONLY _LIBCPP_THREAD_SAFETY_ANNOTATION(scoped_lockable) lock_guard { public: typedef _Mutex mutex_type; @@ -81,13 +89,13 @@ public: _LIBCPP_INLINE_VISIBILITY - explicit lock_guard(mutex_type& __m) + explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m)) : __m_(__m) {__m_.lock();} _LIBCPP_INLINE_VISIBILITY - lock_guard(mutex_type& __m, adopt_lock_t) + lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_THREAD_SAFETY_ANNOTATION(requires_capability(__m)) : __m_(__m) {} _LIBCPP_INLINE_VISIBILITY - ~lock_guard() {__m_.unlock();} + ~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();} private: lock_guard(lock_guard const&);// = delete; Index: test/libcxx/test/format.py =================================================================== --- test/libcxx/test/format.py +++ test/libcxx/test/format.py @@ -167,7 +167,7 @@ # when using Clang. extra_flags = [] if self.cxx.type != 'gcc': - extra_flags += ['-fsyntax-only'] + extra_flags += ['-fsyntax-only', '-Werror=thread-safety'] if use_verify: extra_flags += ['-Xclang', '-verify', '-Xclang', '-verify-ignore-unexpected=note'] Index: test/libcxx/thread/thread.mutex/thread_safety_access_guarded_without_lock.fail.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_access_guarded_without_lock.fail.cpp @@ -0,0 +1,16 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. +#error Test only supported on clang versions that support the acquire_capability annotation +#endif + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; +int foo __attribute__((guarded_by(m))); + +int main() { + foo++; // expected-error{{writing variable 'foo' requires holding mutex 'm' exclusively}} +} Index: test/libcxx/thread/thread.mutex/thread_safety_annotations_not_enabled.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_annotations_not_enabled.cpp @@ -0,0 +1,13 @@ +// This test does not define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS so it +// should compile without any warnings or errors even though this pattern is not +// understood by the thread safety annotations. + +#include + +int main() { + std::mutex m; + m.lock(); + { + std::unique_lock g(m, std::adopt_lock); + } +} Index: test/libcxx/thread/thread.mutex/thread_safety_call_requires_capability_without_having.fail.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_call_requires_capability_without_having.fail.cpp @@ -0,0 +1,20 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. +#error Test only supported on clang versions that support the acquire_capability annotation +#endif + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; +int foo __attribute__((guarded_by(m))); + +void increment() __attribute__((requires_capability(m))) { + foo++; +} + +int main() { + increment(); // expected-error{{calling function 'increment' requires holding mutex 'm' exclusively}} +} Index: test/libcxx/thread/thread.mutex/thread_safety_lock_guard.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_lock_guard.cpp @@ -0,0 +1,17 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; +int foo __attribute__((guarded_by(m))); + +int main() { + std::lock_guard lock(m); + foo++; +} + +#endif Index: test/libcxx/thread/thread.mutex/thread_safety_lock_unlock.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_lock_unlock.cpp @@ -0,0 +1,18 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; +int foo __attribute__((guarded_by(m))); + +int main() { + m.lock(); + foo++; + m.unlock(); +} + +#endif Index: test/libcxx/thread/thread.mutex/thread_safety_missing_unlock.fail.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_missing_unlock.fail.cpp @@ -0,0 +1,15 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. +#error Test only supported on clang versions that support the acquire_capability annotation +#endif + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; + +int main() { + m.lock(); +} // expected-error {{mutex 'm' is still held at the end of function}} Index: test/libcxx/thread/thread.mutex/thread_safety_requires_capability.cpp =================================================================== --- /dev/null +++ test/libcxx/thread/thread.mutex/thread_safety_requires_capability.cpp @@ -0,0 +1,22 @@ +#if !defined(__clang__) || !__has_attribute(acquire_capability) +// This test is only meaningful on versions of clang that understand thread +// safety annotations. + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include + +std::mutex m; +int foo __attribute__((guarded_by(m))); + +void increment() __attribute__((requires_capability(m))) { + foo++; +} + +int main() { + m.lock(); + increment(); + m.unlock(); +} + +#endif