[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:
Hristo Hristov 2025-11-27 00:04:27 +02:00 committed by GitHub
parent 3d596ad092
commit d5778a7ff5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 173 additions and 53 deletions

View File

@ -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

View File

@ -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");

View File

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

View File

@ -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)) {

View File

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

View File

@ -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()) {

View File

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

View 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
}

View File

@ -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}}
}