[libc++] Applied [[nodiscard]] to concurrency (partially) (#169463)
`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue. - https://libcxx.llvm.org/CodingGuidelines.html#apply-nodiscard-where-relevant The following utilities have been annotated in this patch: - [x] `<barrier>` - [x] `<condition_variable>` - [x] `<latch>` - [x] `<mutex>` - [x] `<semaphore>` - [x] `<thread>` N.B. Some classes don't provide all specified methods, which were not annotated.
This commit is contained in:
parent
3d596ad092
commit
d5778a7ff5
@ -170,7 +170,7 @@ public:
|
||||
wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);
|
||||
|
||||
typedef __libcpp_condvar_t* native_handle_type;
|
||||
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }
|
||||
|
||||
private:
|
||||
void
|
||||
|
||||
@ -37,11 +37,11 @@ public:
|
||||
# endif
|
||||
|
||||
_LIBCPP_ACQUIRE_CAPABILITY() void lock();
|
||||
_LIBCPP_TRY_ACQUIRE_CAPABILITY(true) bool try_lock() _NOEXCEPT;
|
||||
[[__nodiscard__]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) bool try_lock() _NOEXCEPT;
|
||||
_LIBCPP_RELEASE_CAPABILITY void unlock() _NOEXCEPT;
|
||||
|
||||
typedef __libcpp_mutex_t* native_handle_type;
|
||||
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
|
||||
};
|
||||
|
||||
static_assert(is_nothrow_default_constructible<mutex>::value, "the default constructor for std::mutex must be nothrow");
|
||||
|
||||
@ -242,13 +242,13 @@ public:
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI void swap(thread& __t) _NOEXCEPT { std::swap(__t_, __t.__t_); }
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI bool joinable() const _NOEXCEPT { return !__libcpp_thread_isnull(&__t_); }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool joinable() const _NOEXCEPT { return !__libcpp_thread_isnull(&__t_); }
|
||||
void join();
|
||||
void detach();
|
||||
_LIBCPP_HIDE_FROM_ABI id get_id() const _NOEXCEPT { return __libcpp_thread_get_id(&__t_); }
|
||||
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() _NOEXCEPT { return __t_; }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI id get_id() const _NOEXCEPT { return __libcpp_thread_get_id(&__t_); }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() _NOEXCEPT { return __t_; }
|
||||
|
||||
static unsigned hardware_concurrency() _NOEXCEPT;
|
||||
[[__nodiscard__]] static unsigned hardware_concurrency() _NOEXCEPT;
|
||||
};
|
||||
|
||||
inline _LIBCPP_HIDE_FROM_ABI void swap(thread& __x, thread& __y) _NOEXCEPT { __x.swap(__y); }
|
||||
|
||||
@ -158,7 +158,9 @@ class barrier {
|
||||
public:
|
||||
using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
|
||||
|
||||
static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return __barrier_base<_CompletionF>::max(); }
|
||||
[[nodiscard]] static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept {
|
||||
return __barrier_base<_CompletionF>::max();
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI explicit barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
|
||||
: __b_(__count, std::move(__completion)) {
|
||||
|
||||
@ -70,7 +70,9 @@ class latch {
|
||||
atomic<ptrdiff_t> __a_;
|
||||
|
||||
public:
|
||||
static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return numeric_limits<ptrdiff_t>::max(); }
|
||||
[[nodiscard]] static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept {
|
||||
return numeric_limits<ptrdiff_t>::max();
|
||||
}
|
||||
|
||||
inline _LIBCPP_HIDE_FROM_ABI constexpr explicit latch(ptrdiff_t __expected) : __a_(__expected) {
|
||||
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
|
||||
@ -97,7 +99,7 @@ public:
|
||||
if (__old == __update)
|
||||
__a_.notify_all();
|
||||
}
|
||||
inline _LIBCPP_HIDE_FROM_ABI bool try_wait() const noexcept {
|
||||
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool try_wait() const noexcept {
|
||||
auto __value = __a_.load(memory_order_acquire);
|
||||
return try_wait_impl(__value);
|
||||
}
|
||||
|
||||
@ -229,12 +229,12 @@ public:
|
||||
recursive_mutex& operator=(const recursive_mutex&) = delete;
|
||||
|
||||
void lock();
|
||||
bool try_lock() _NOEXCEPT;
|
||||
[[__nodiscard__]] bool try_lock() _NOEXCEPT;
|
||||
void unlock() _NOEXCEPT;
|
||||
|
||||
typedef __libcpp_recursive_mutex_t* native_handle_type;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
|
||||
};
|
||||
|
||||
class _LIBCPP_EXPORTED_FROM_ABI timed_mutex {
|
||||
@ -251,14 +251,14 @@ public:
|
||||
|
||||
public:
|
||||
void lock();
|
||||
bool try_lock() _NOEXCEPT;
|
||||
[[__nodiscard__]] bool try_lock() _NOEXCEPT;
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __d) {
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __d) {
|
||||
return try_lock_until(chrono::steady_clock::now() + __d);
|
||||
}
|
||||
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) {
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) {
|
||||
using namespace chrono;
|
||||
unique_lock<mutex> __lk(__m_);
|
||||
bool __no_timeout = _Clock::now() < __t;
|
||||
@ -288,14 +288,14 @@ public:
|
||||
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
|
||||
|
||||
void lock();
|
||||
bool try_lock() _NOEXCEPT;
|
||||
[[__nodiscard__]] bool try_lock() _NOEXCEPT;
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __d) {
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool try_lock_for(const chrono::duration<_Rep, _Period>& __d) {
|
||||
return try_lock_until(chrono::steady_clock::now() + __d);
|
||||
}
|
||||
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) {
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) {
|
||||
using namespace chrono;
|
||||
__thread_id __id = this_thread::get_id();
|
||||
unique_lock<mutex> __lk(__m_);
|
||||
@ -320,7 +320,7 @@ public:
|
||||
};
|
||||
|
||||
template <class _L0, class _L1>
|
||||
_LIBCPP_NO_THREAD_SAFETY_ANALYSIS _LIBCPP_HIDE_FROM_ABI int try_lock(_L0& __l0, _L1& __l1) {
|
||||
[[__nodiscard__]] _LIBCPP_NO_THREAD_SAFETY_ANALYSIS _LIBCPP_HIDE_FROM_ABI int try_lock(_L0& __l0, _L1& __l1) {
|
||||
unique_lock<_L0> __u0(__l0, try_to_lock_t());
|
||||
if (__u0.owns_lock()) {
|
||||
if (__l1.try_lock()) {
|
||||
@ -335,7 +335,8 @@ _LIBCPP_NO_THREAD_SAFETY_ANALYSIS _LIBCPP_HIDE_FROM_ABI int try_lock(_L0& __l0,
|
||||
# ifndef _LIBCPP_CXX03_LANG
|
||||
|
||||
template <class _L0, class _L1, class _L2, class... _L3>
|
||||
_LIBCPP_NO_THREAD_SAFETY_ANALYSIS _LIBCPP_HIDE_FROM_ABI int try_lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3) {
|
||||
[[__nodiscard__]] _LIBCPP_NO_THREAD_SAFETY_ANALYSIS
|
||||
_LIBCPP_HIDE_FROM_ABI int try_lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3) {
|
||||
int __r = 0;
|
||||
unique_lock<_L0> __u0(__l0, try_to_lock);
|
||||
if (__u0.owns_lock()) {
|
||||
|
||||
@ -133,7 +133,7 @@ class counting_semaphore {
|
||||
public:
|
||||
static_assert(__least_max_value >= 0, "The least maximum value must be a positive number");
|
||||
|
||||
static constexpr ptrdiff_t max() noexcept { return __least_max_value; }
|
||||
[[nodiscard]] static constexpr ptrdiff_t max() noexcept { return __least_max_value; }
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore_(__count) {
|
||||
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
|
||||
@ -156,12 +156,12 @@ public:
|
||||
}
|
||||
_LIBCPP_HIDE_FROM_ABI void acquire() { __semaphore_.acquire(); }
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_acquire_for(chrono::duration<_Rep, _Period> const& __rel_time) {
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool try_acquire_for(chrono::duration<_Rep, _Period> const& __rel_time) {
|
||||
return __semaphore_.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
|
||||
}
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_acquire() { return __semaphore_.try_acquire(); }
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool try_acquire() { return __semaphore_.try_acquire(); }
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_HIDE_FROM_ABI bool try_acquire_until(chrono::time_point<_Clock, _Duration> const& __abs_time) {
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool try_acquire_until(chrono::time_point<_Clock, _Duration> const& __abs_time) {
|
||||
auto const __current = _Clock::now();
|
||||
if (__current >= __abs_time)
|
||||
return try_acquire();
|
||||
|
||||
144
libcxx/test/libcxx/thread/nodiscard.verify.cpp
Normal file
144
libcxx/test/libcxx/thread/nodiscard.verify.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
// UNSUPPORTED: no-threads
|
||||
|
||||
// Check that functions are marked [[nodiscard]]
|
||||
|
||||
#include <chrono>
|
||||
#include <barrier>
|
||||
#include <latch>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
const auto timePoint = std::chrono::steady_clock::now();
|
||||
|
||||
void test() {
|
||||
// Threads
|
||||
{
|
||||
std::thread th;
|
||||
|
||||
th.joinable(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
th.get_id(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
th.native_handle(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
th.hardware_concurrency(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
#if TEST_STD_VER >= 20
|
||||
{
|
||||
std::jthread jt;
|
||||
|
||||
jt.joinable(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
jt.get_id(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
jt.native_handle(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
jt.get_stop_source(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
jt.get_stop_token(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
jt.hardware_concurrency(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mutual exclusion
|
||||
|
||||
{ // <mutex>
|
||||
std::mutex m;
|
||||
|
||||
m.try_lock(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.native_handle(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
{
|
||||
std::recursive_mutex m;
|
||||
|
||||
m.try_lock(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.native_handle(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
{
|
||||
std::timed_mutex m;
|
||||
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock();
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock_for(std::chrono::nanoseconds{82});
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock_until(timePoint);
|
||||
}
|
||||
{
|
||||
std::recursive_timed_mutex m;
|
||||
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock();
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock_for(std::chrono::nanoseconds{82});
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
m.try_lock_until(timePoint);
|
||||
}
|
||||
{
|
||||
std::mutex m1;
|
||||
std::mutex m2;
|
||||
std::mutex m3;
|
||||
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::try_lock(m1, m2);
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
std::try_lock(m1, m2, m3);
|
||||
}
|
||||
|
||||
// Condition variables
|
||||
|
||||
{ // <condition_variable>
|
||||
std::condition_variable cv;
|
||||
|
||||
cv.native_handle(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
|
||||
// Semaphores
|
||||
|
||||
{ // <semaphore>
|
||||
std::counting_semaphore<> cs{0};
|
||||
|
||||
cs.max(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
cs.try_acquire_for(std::chrono::nanoseconds{82});
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
cs.try_acquire();
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
cs.try_acquire_until(timePoint);
|
||||
|
||||
std::binary_semaphore bs{0};
|
||||
|
||||
bs.max(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
bs.try_acquire_for(std::chrono::nanoseconds{82});
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
bs.try_acquire();
|
||||
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
bs.try_acquire_until(timePoint);
|
||||
}
|
||||
|
||||
// Latches and barriers
|
||||
|
||||
{ // <barrier>
|
||||
std::barrier<> b{94};
|
||||
|
||||
b.max(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
{ // <latch>
|
||||
std::latch l{94};
|
||||
|
||||
l.max(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
l.try_wait(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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: no-threads
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
|
||||
// [[nodiscard]] bool joinable() const noexcept;
|
||||
// [[nodiscard]] id get_id() const noexcept;
|
||||
// [[nodiscard]] native_handle_type native_handle();
|
||||
// [[nodiscard]] stop_source get_stop_source() noexcept;
|
||||
// [[nodiscard]] stop_token get_stop_token() const noexcept;
|
||||
// [[nodiscard]] static unsigned int hardware_concurrency() noexcept;
|
||||
|
||||
#include <thread>
|
||||
|
||||
void test() {
|
||||
std::jthread jt;
|
||||
jt.joinable(); // expected-warning {{ignoring return value of function}}
|
||||
jt.get_id(); // expected-warning {{ignoring return value of function}}
|
||||
jt.native_handle(); // expected-warning {{ignoring return value of function}}
|
||||
jt.get_stop_source(); // expected-warning {{ignoring return value of function}}
|
||||
jt.get_stop_token(); // expected-warning {{ignoring return value of function}}
|
||||
jt.hardware_concurrency(); // expected-warning {{ignoring return value of function}}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user