Strengthen exception specifications for [thread] types (#3388)

This commit is contained in:
A. Jiang 2023-02-14 08:23:21 +08:00 коммит произвёл GitHub
Родитель 9ae1b3f1fa
Коммит ade0d6630e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 111 добавлений и 43 удалений

Просмотреть файл

@ -160,7 +160,7 @@ private:
struct _Cv_any_notify_all {
condition_variable_any* _This;
explicit _Cv_any_notify_all(condition_variable_any* _This_) : _This{_This_} {}
explicit _Cv_any_notify_all(condition_variable_any* _This_) noexcept : _This{_This_} {}
_Cv_any_notify_all(const _Cv_any_notify_all&) = delete;
_Cv_any_notify_all& operator=(const _Cv_any_notify_all&) = delete;
@ -273,7 +273,8 @@ private:
}
};
_EXPORT_STD inline void notify_all_at_thread_exit(condition_variable& _Cnd, unique_lock<mutex> _Lck) {
_EXPORT_STD inline void notify_all_at_thread_exit(condition_variable& _Cnd, unique_lock<mutex> _Lck) noexcept
/* strengthened */ {
// register _Cnd for release at thread exit
_Cnd._Register(_Lck, nullptr);
}

Просмотреть файл

@ -48,16 +48,8 @@ public:
_Check_C_return(_Mtx_lock(_Mymtx()));
}
_NODISCARD_TRY_CHANGE_STATE bool try_lock() {
const auto _Res = _Mtx_trylock(_Mymtx());
switch (_Res) {
case _Thrd_success:
return true;
case _Thrd_busy:
return false;
default:
_Throw_C_error(_Res);
}
_NODISCARD_TRY_CHANGE_STATE bool try_lock() noexcept /* strengthened */ {
return _Mtx_trylock(_Mymtx()) == _Thrd_success;
}
void unlock() noexcept /* strengthened */ {
@ -66,7 +58,7 @@ public:
using native_handle_type = void*;
_NODISCARD native_handle_type native_handle() {
_NODISCARD native_handle_type native_handle() noexcept /* strengthened */ {
return _Mtx_getconcrtcs(_Mymtx());
}
@ -92,7 +84,8 @@ public:
_EXPORT_STD class recursive_mutex : public _Mutex_base { // class for recursive mutual exclusion
public:
recursive_mutex() : _Mutex_base(_Mtx_recursive) {}
recursive_mutex() noexcept // strengthened
: _Mutex_base(_Mtx_recursive) {}
_NODISCARD_TRY_CHANGE_STATE bool try_lock() noexcept {
return _Mutex_base::try_lock();
@ -123,7 +116,7 @@ class unique_lock { // whizzy class with destructor that unlocks mutex
public:
using mutex_type = _Mutex;
unique_lock() noexcept : _Pmtx(nullptr), _Owns(false) {}
unique_lock() noexcept = default;
_NODISCARD_CTOR_LOCK explicit unique_lock(_Mutex& _Mtx)
: _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // construct and lock
@ -131,7 +124,7 @@ public:
_Owns = true;
}
_NODISCARD_CTOR_LOCK unique_lock(_Mutex& _Mtx, adopt_lock_t)
_NODISCARD_CTOR_LOCK unique_lock(_Mutex& _Mtx, adopt_lock_t) noexcept // strengthened
: _Pmtx(_STD addressof(_Mtx)), _Owns(true) {} // construct and assume already locked
unique_lock(_Mutex& _Mtx, defer_lock_t) noexcept
@ -255,8 +248,8 @@ public:
}
private:
_Mutex* _Pmtx;
bool _Owns;
_Mutex* _Pmtx = nullptr;
bool _Owns = false;
void _Validate() const { // check if the mutex can be locked
if (!_Pmtx) {
@ -455,7 +448,8 @@ public:
_MyMutex.lock();
}
lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {} // construct but don't lock
lock_guard(_Mutex& _Mtx, adopt_lock_t) noexcept // strengthened
: _MyMutex(_Mtx) {} // construct but don't lock
~lock_guard() noexcept {
_MyMutex.unlock();
@ -476,7 +470,8 @@ public:
_STD lock(_Mtxes...);
}
explicit scoped_lock(adopt_lock_t, _Mutexes&... _Mtxes) : _MyMutexes(_Mtxes...) {} // construct but don't lock
explicit scoped_lock(adopt_lock_t, _Mutexes&... _Mtxes) noexcept // strengthened
: _MyMutexes(_Mtxes...) {} // construct but don't lock
~scoped_lock() noexcept {
_STD apply([](_Mutexes&... _Mtxes) { (..., (void) _Mtxes.unlock()); }, _MyMutexes);
@ -498,7 +493,8 @@ public:
_MyMutex.lock();
}
explicit scoped_lock(adopt_lock_t, _Mutex& _Mtx) : _MyMutex(_Mtx) {} // construct but don't lock
explicit scoped_lock(adopt_lock_t, _Mutex& _Mtx) noexcept // strengthened
: _MyMutex(_Mtx) {} // construct but don't lock
~scoped_lock() noexcept {
_MyMutex.unlock();
@ -514,9 +510,8 @@ private:
template <>
class scoped_lock<> {
public:
explicit scoped_lock() {}
explicit scoped_lock(adopt_lock_t) {}
~scoped_lock() noexcept {}
explicit scoped_lock() = default;
explicit scoped_lock(adopt_lock_t) noexcept /* strengthened */ {}
scoped_lock(const scoped_lock&) = delete;
scoped_lock& operator=(const scoped_lock&) = delete;
@ -600,7 +595,7 @@ _EXPORT_STD class condition_variable { // class for waiting for conditions
public:
using native_handle_type = _Cnd_t;
condition_variable() {
condition_variable() noexcept /* strengthened */ {
_Cnd_init_in_situ(_Mycnd());
}
@ -619,7 +614,7 @@ public:
_Cnd_broadcast(_Mycnd());
}
void wait(unique_lock<mutex>& _Lck) { // wait for signal
void wait(unique_lock<mutex>& _Lck) noexcept /* strengthened */ { // wait for signal
// Nothing to do to comply with LWG-2135 because std::mutex lock/unlock are nothrow
_Cnd_wait(_Mycnd(), _Lck.mutex()->_Mymtx());
}
@ -711,15 +706,15 @@ public:
return _Wait_until1(_Lck, _Abs_time, _Pred);
}
_NODISCARD native_handle_type native_handle() {
_NODISCARD native_handle_type native_handle() noexcept /* strengthened */ {
return _Mycnd();
}
void _Register(unique_lock<mutex>& _Lck, int* _Ready) { // register this object for release at thread exit
void _Register(unique_lock<mutex>& _Lck, int* _Ready) noexcept { // register this object for release at thread exit
_Cnd_register_at_thread_exit(_Mycnd(), _Lck.release()->_Mymtx(), _Ready);
}
void _Unregister(mutex& _Mtx) { // unregister this object for release at thread exit
void _Unregister(mutex& _Mtx) noexcept { // unregister this object for release at thread exit
_Cnd_unregister_at_thread_exit(_Mtx._Mymtx());
}
@ -765,14 +760,14 @@ private:
struct _UInt_is_zero {
const unsigned int& _UInt;
_NODISCARD bool operator()() const {
_NODISCARD bool operator()() const noexcept {
return _UInt == 0;
}
};
_EXPORT_STD class timed_mutex { // class for timed mutual exclusion
public:
timed_mutex() noexcept : _My_locked(0) {}
timed_mutex() = default;
timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;
@ -786,7 +781,7 @@ public:
_My_locked = UINT_MAX;
}
_NODISCARD_TRY_CHANGE_STATE bool try_lock() noexcept { // try to lock the mutex
_NODISCARD_TRY_CHANGE_STATE bool try_lock() noexcept /* strengthened */ { // try to lock the mutex
lock_guard<mutex> _Lock(_My_mutex);
if (_My_locked != 0) {
return false;
@ -838,12 +833,12 @@ public:
private:
mutex _My_mutex;
condition_variable _My_cond;
unsigned int _My_locked;
unsigned int _My_locked = 0;
};
_EXPORT_STD class recursive_timed_mutex { // class for recursive timed mutual exclusion
public:
recursive_timed_mutex() noexcept : _My_locked(0) {}
recursive_timed_mutex() = default;
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
@ -953,7 +948,7 @@ public:
private:
mutex _My_mutex;
condition_variable _My_cond;
unsigned int _My_locked;
unsigned int _My_locked = 0;
thread::id _My_owner;
};
_STD_END

Просмотреть файл

@ -72,7 +72,7 @@ private:
static constexpr _Read_cnt_t _Max_readers = static_cast<_Read_cnt_t>(-1);
public:
shared_timed_mutex() noexcept /* strengthened */ {}
shared_timed_mutex() = default;
shared_timed_mutex(const shared_timed_mutex&) = delete;
shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
@ -238,7 +238,7 @@ public:
_NODISCARD_CTOR_LOCK shared_lock(mutex_type& _Mtx, try_to_lock_t)
: _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared()) {} // construct with mutex and try to lock shared
_NODISCARD_CTOR_LOCK shared_lock(mutex_type& _Mtx, adopt_lock_t)
_NODISCARD_CTOR_LOCK shared_lock(mutex_type& _Mtx, adopt_lock_t) noexcept // strengthened
: _Pmtx(_STD addressof(_Mtx)), _Owns(true) {} // construct with mutex and adopt ownership
template <class _Rep, class _Period>

Просмотреть файл

@ -207,12 +207,12 @@ namespace this_thread {
class thread::id { // thread id
public:
id() noexcept : _Id(0) {} // id for no thread
id() noexcept = default; // id for no thread
private:
id(_Thrd_id_t _Other_id) : _Id(_Other_id) {}
explicit id(_Thrd_id_t _Other_id) noexcept : _Id(_Other_id) {}
_Thrd_id_t _Id;
_Thrd_id_t _Id = 0;
friend thread::id thread::get_id() const noexcept;
friend thread::id this_thread::get_id() noexcept;
@ -228,11 +228,11 @@ private:
};
_NODISCARD inline thread::id thread::get_id() const noexcept {
return _Thr._Id;
return thread::id{_Thr._Id};
}
_EXPORT_STD _NODISCARD inline thread::id this_thread::get_id() noexcept {
return _Thrd_id();
return thread::id{_Thrd_id()};
}
_EXPORT_STD inline void swap(thread& _Left, thread& _Right) noexcept {

Просмотреть файл

@ -664,6 +664,7 @@ std/time/time.syn/formatter.year_month_day_last.pass.cpp:0 FAIL
std/time/time.syn/formatter.year_month_weekday.pass.cpp:0 FAIL
# unused-variable warning
std/thread/thread.mutex/thread.lock/thread.lock.scoped/mutex.pass.cpp FAIL
std/thread/thread.mutex/thread.mutex.requirements/thread.shared_mutex.requirements/thread.shared_mutex.class/default.pass.cpp:1 FAIL

Просмотреть файл

@ -664,6 +664,7 @@ time\time.syn\formatter.year_month_day_last.pass.cpp
time\time.syn\formatter.year_month_weekday.pass.cpp
# unused-variable warning
thread\thread.mutex\thread.lock\thread.lock.scoped\mutex.pass.cpp
thread\thread.mutex\thread.mutex.requirements\thread.shared_mutex.requirements\thread.shared_mutex.class\default.pass.cpp

Просмотреть файл

@ -9,6 +9,7 @@
#include <shared_mutex>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
@ -35,10 +36,39 @@ STATIC_ASSERT(is_nothrow_destructible_v<shared_lock<shared_timed_mutex>>);
STATIC_ASSERT(is_nothrow_destructible_v<condition_variable>);
STATIC_ASSERT(is_nothrow_default_constructible_v<mutex>); // N4928 [thread.mutex.class]
STATIC_ASSERT(is_nothrow_default_constructible_v<recursive_mutex>); // strengthened
STATIC_ASSERT(is_nothrow_default_constructible_v<timed_mutex>); // strengthened
STATIC_ASSERT(is_nothrow_default_constructible_v<recursive_timed_mutex>); // strengthened
STATIC_ASSERT(is_nothrow_default_constructible_v<shared_mutex>); // strengthened
STATIC_ASSERT(is_nothrow_default_constructible_v<shared_timed_mutex>); // strengthened
STATIC_ASSERT(is_nothrow_default_constructible_v<shared_lock<shared_mutex>>); // N4928 [thread.lock.shared.cons]/1
STATIC_ASSERT(is_nothrow_default_constructible_v<shared_lock<shared_timed_mutex>>); // N4928 [thread.lock.shared.cons]/1
STATIC_ASSERT(is_nothrow_default_constructible_v<condition_variable>); // strengthened
STATIC_ASSERT(is_nothrow_constructible_v<shared_lock<shared_mutex>, shared_mutex&, adopt_lock_t>); // strengthened
STATIC_ASSERT(
is_nothrow_constructible_v<shared_lock<shared_mutex>, shared_mutex&, const adopt_lock_t&>); // strengthened
STATIC_ASSERT(
is_nothrow_constructible_v<shared_lock<shared_timed_mutex>, shared_timed_mutex&, adopt_lock_t>); // strengthened
STATIC_ASSERT(is_nothrow_constructible_v<shared_lock<shared_timed_mutex>, shared_timed_mutex&,
const adopt_lock_t&>); // strengthened
// Also test strengthened exception specification for native_handle().
STATIC_ASSERT(noexcept(declval<thread&>().native_handle()));
#if _HAS_CXX20
STATIC_ASSERT(noexcept(declval<jthread&>().native_handle()));
#endif // _HAS_CXX20
STATIC_ASSERT(noexcept(declval<mutex&>().native_handle()));
STATIC_ASSERT(noexcept(declval<recursive_mutex&>().native_handle()));
STATIC_ASSERT(noexcept(declval<shared_mutex&>().native_handle()));
STATIC_ASSERT(noexcept(declval<condition_variable&>().native_handle()));
// Also test mandatory and strengthened exception specification for try_lock().
STATIC_ASSERT(noexcept(declval<mutex&>().try_lock())); // strengthened
STATIC_ASSERT(noexcept(declval<recursive_mutex&>().try_lock())); // N4928 [thread.mutex.recursive]
STATIC_ASSERT(noexcept(declval<timed_mutex&>().try_lock())); // strengthened
STATIC_ASSERT(noexcept(declval<recursive_timed_mutex&>().try_lock())); // N4928 [thread.timedmutex.recursive]
STATIC_ASSERT(noexcept(declval<shared_mutex&>().try_lock())); // strengthened
void join_and_clear(vector<thread>& threads) {
for (auto& t : threads) {

Просмотреть файл

@ -9,8 +9,40 @@
#include <type_traits>
#include <utility>
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
using namespace std;
// nothrow-destructibility required by N4928 [res.on.exception.handling]/3
STATIC_ASSERT(is_nothrow_destructible_v<lock_guard<mutex>>);
STATIC_ASSERT(is_nothrow_destructible_v<unique_lock<mutex>>);
#if _HAS_CXX17
STATIC_ASSERT(is_nothrow_destructible_v<scoped_lock<>>);
STATIC_ASSERT(is_nothrow_destructible_v<scoped_lock<mutex>>);
STATIC_ASSERT(is_nothrow_destructible_v<scoped_lock<mutex, recursive_mutex>>);
#endif // _HAS_CXX17
// Test mandatory and strengthened exception specification for default construction
STATIC_ASSERT(is_nothrow_default_constructible_v<unique_lock<mutex>>);
#if _HAS_CXX17
STATIC_ASSERT(is_nothrow_default_constructible_v<scoped_lock<>>); // strengthened
#endif // _HAS_CXX17
// Test strengthened exception specification for adopt_lock construction
STATIC_ASSERT(is_nothrow_constructible_v<lock_guard<mutex>, mutex&, adopt_lock_t>);
STATIC_ASSERT(is_nothrow_constructible_v<lock_guard<mutex>, mutex&, const adopt_lock_t&>);
STATIC_ASSERT(is_nothrow_constructible_v<unique_lock<mutex>, mutex&, adopt_lock_t>);
STATIC_ASSERT(is_nothrow_constructible_v<unique_lock<mutex>, mutex&, const adopt_lock_t&>);
#if _HAS_CXX17
STATIC_ASSERT(is_nothrow_constructible_v<scoped_lock<>, adopt_lock_t>);
STATIC_ASSERT(is_nothrow_constructible_v<scoped_lock<>, const adopt_lock_t&>);
STATIC_ASSERT(is_nothrow_constructible_v<scoped_lock<mutex>, adopt_lock_t, mutex&>);
STATIC_ASSERT(is_nothrow_constructible_v<scoped_lock<mutex>, const adopt_lock_t&, mutex&>);
STATIC_ASSERT(is_nothrow_constructible_v<scoped_lock<mutex, recursive_mutex>, adopt_lock_t, mutex&, recursive_mutex&>);
STATIC_ASSERT(
is_nothrow_constructible_v<scoped_lock<mutex, recursive_mutex>, const adopt_lock_t&, mutex&, recursive_mutex&>);
#endif // _HAS_CXX17
// LOCK ORDERING: g_coutMutex is locked after all other locks and synchronizes
// access to cout.
using TestMutex = mutex;
@ -161,7 +193,7 @@ void exec_test_scoped_lock_adopts_one_mutex() {
#if _HAS_CXX17
// Special case for 0 mutex types.
void exec_test_scoped_lock_compiles_with_no_mutexes() {
scoped_lock<> takeNoLocks;
[[maybe_unused]] scoped_lock<> takeNoLocks;
VERIFY_UNOWNED(g_mutexA);
VERIFY_UNOWNED(g_mutexB);
VERIFY_UNOWNED(g_mutexC);

Просмотреть файл

@ -172,6 +172,14 @@ void normal_tests() {
test_case_condition_variable_any_less_accurate_time_points();
}
// Test strengthened exception specification of condition_variable::wait
static_assert(noexcept(declval<condition_variable&>().wait(declval<unique_lock<mutex>&>())),
"condition_variable::wait that takes no predicate should be noexcept");
// Test strengthened exception specification of notify_all_at_thread_exit
static_assert(noexcept(notify_all_at_thread_exit(declval<condition_variable&>(), declval<unique_lock<mutex>>())),
"notify_all_at_thread_exit should be noexcept");
int main(int argc, char* argv[]) {
std_testing::death_test_executive exec(normal_tests);
exec.add_death_tests({test_case_condition_variable_any_lock_ex_should_terminate});