зеркало из https://github.com/microsoft/STL.git
Strengthen exception specifications for [thread] types (#3388)
This commit is contained in:
Родитель
9ae1b3f1fa
Коммит
ade0d6630e
|
@ -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});
|
||||
|
|
Загрузка…
Ссылка в новой задаче