Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanups for mutex utilities #3912

Merged
merged 12 commits into from
Aug 11, 2023
8 changes: 6 additions & 2 deletions stl/inc/xthreads.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,21 @@ enum { // mutex types
_Mtx_recursive = 0x100
};

#ifdef _CRTBLD
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_init(_Mtx_t*, int);
_CRTIMP2_PURE void __cdecl _Mtx_destroy(_Mtx_t);
#endif // _CRTBLD
_CRTIMP2_PURE void __cdecl _Mtx_init_in_situ(_Mtx_t, int);
_CRTIMP2_PURE void __cdecl _Mtx_destroy_in_situ(_Mtx_t);
_CRTIMP2_PURE int __cdecl _Mtx_current_owns(_Mtx_t);
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_lock(_Mtx_t);
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_trylock(_Mtx_t);
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_timedlock(_Mtx_t, const _timespec64*);
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_unlock(_Mtx_t); // TRANSITION, ABI: Always succeeds
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

_CRTIMP2_PURE void* __cdecl _Mtx_getconcrtcs(_Mtx_t);
#ifdef _CRTBLD
_CRTIMP2_PURE void __cdecl _Mtx_clear_owner(_Mtx_t);
_CRTIMP2_PURE void __cdecl _Mtx_reset_owner(_Mtx_t);
#endif // _CRTBLD

// shared mutex
// these declarations must be in sync with those in sharedmutex.cpp
Expand All @@ -120,8 +122,10 @@ void __cdecl _Smtx_unlock_exclusive(_Smtx_t*);
void __cdecl _Smtx_unlock_shared(_Smtx_t*);

// condition variables
#ifdef _CRTBLD
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_init(_Cnd_t*);
_CRTIMP2_PURE void __cdecl _Cnd_destroy(_Cnd_t);
#endif // _CRTBLD
_CRTIMP2_PURE void __cdecl _Cnd_init_in_situ(_Cnd_t);
_CRTIMP2_PURE void __cdecl _Cnd_destroy_in_situ(_Cnd_t);
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_wait(_Cnd_t, _Mtx_t); // TRANSITION, ABI: Always succeeds
Expand Down
21 changes: 13 additions & 8 deletions stl/src/cond.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include "primitives.hpp"

_EXTERN_C

struct _Cnd_internal_imp_t { // condition variable implementation for ConcRT
typename std::_Aligned_storage<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment>::type cv;

Expand All @@ -20,13 +22,13 @@ struct _Cnd_internal_imp_t { // condition variable implementation for ConcRT
}
};

void _Cnd_init_in_situ(const _Cnd_t cond) { // initialize condition variable in situ
_CRTIMP2_PURE void __cdecl _Cnd_init_in_situ(const _Cnd_t cond) { // initialize condition variable in situ
Concurrency::details::create_stl_condition_variable(cond->_get_cv());
}

void _Cnd_destroy_in_situ(_Cnd_t) {} // destroy condition variable in situ
_CRTIMP2_PURE void __cdecl _Cnd_destroy_in_situ(_Cnd_t) {} // destroy condition variable in situ

_Thrd_result _Cnd_init(_Cnd_t* const pcond) { // initialize
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_init(_Cnd_t* const pcond) { // initialize
*pcond = nullptr;

const auto cond = static_cast<_Cnd_t>(_calloc_crt(1, sizeof(_Cnd_internal_imp_t)));
Expand All @@ -39,14 +41,14 @@ _Thrd_result _Cnd_init(_Cnd_t* const pcond) { // initialize
return _Thrd_result::_Success;
}

void _Cnd_destroy(const _Cnd_t cond) { // clean up
_CRTIMP2_PURE void __cdecl _Cnd_destroy(const _Cnd_t cond) { // clean up
if (cond) { // something to do, do it
_Cnd_destroy_in_situ(cond);
_free_crt(cond);
}
}

_Thrd_result _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx) { // wait until signaled
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx) { // wait until signaled
const auto cs = &mtx->_Critical_section;
_Mtx_clear_owner(mtx);
cond->_get_cv()->wait(cs);
Expand All @@ -55,7 +57,8 @@ _Thrd_result _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx) { // wait until sign
}

// wait until signaled or timeout
_Thrd_result _Cnd_timedwait(const _Cnd_t cond, const _Mtx_t mtx, const _timespec64* const target) {
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_timedwait(
const _Cnd_t cond, const _Mtx_t mtx, const _timespec64* const target) {
_Thrd_result res = _Thrd_result::_Success;
const auto cs = &mtx->_Critical_section;
if (target == nullptr) { // no target time specified, wait on mutex
Expand All @@ -77,16 +80,18 @@ _Thrd_result _Cnd_timedwait(const _Cnd_t cond, const _Mtx_t mtx, const _timespec
return res;
}

_Thrd_result _Cnd_signal(const _Cnd_t cond) { // release one waiting thread
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_signal(const _Cnd_t cond) { // release one waiting thread
cond->_get_cv()->notify_one();
return _Thrd_result::_Success; // TRANSITION, ABI: Always succeeds
}

_Thrd_result _Cnd_broadcast(const _Cnd_t cond) { // release all waiting threads
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_broadcast(const _Cnd_t cond) { // release all waiting threads
cond->_get_cv()->notify_all();
return _Thrd_result::_Success; // TRANSITION, ABI: Always succeeds
}

_END_EXTERN_C

/*
* This file is derived from software bearing the following
* restrictions:
Expand Down
23 changes: 12 additions & 11 deletions stl/src/cthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ namespace {
_EXTERN_C

// TRANSITION, ABI: _Thrd_exit() is preserved for binary compatibility
[[noreturn]] _CRTIMP2_PURE void _Thrd_exit(int res) { // terminate execution of calling thread
[[noreturn]] _CRTIMP2_PURE void __cdecl _Thrd_exit(int res) { // terminate execution of calling thread
_endthreadex(res);
}

// TRANSITION, ABI: _Thrd_start() is preserved for binary compatibility
_CRTIMP2_PURE _Thrd_result _Thrd_start(_Thrd_t* thr, _Thrd_callback_t func, void* b) { // start a thread
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_start(_Thrd_t* thr, _Thrd_callback_t func, void* b) { // start a thread
thr->_Hnd = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, func, b, 0, &thr->_Id));
return thr->_Hnd == nullptr ? _Thrd_result::_Error : _Thrd_result::_Success;
}

_Thrd_result _Thrd_join(_Thrd_t thr, int* code) { // returns when thread terminates
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_join(_Thrd_t thr, int* code) { // returns when thread terminates
if (WaitForSingleObjectEx(thr._Hnd, INFINITE, FALSE) == WAIT_FAILED) {
return _Thrd_result::_Error;
}
Expand All @@ -68,11 +68,12 @@ _Thrd_result _Thrd_join(_Thrd_t thr, int* code) { // returns when thread termina
return CloseHandle(thr._Hnd) ? _Thrd_result::_Success : _Thrd_result::_Error;
}

_Thrd_result _Thrd_detach(_Thrd_t thr) { // tell OS to release thread's resources when it terminates
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_detach(_Thrd_t thr) {
// tell OS to release thread's resources when it terminates
return CloseHandle(thr._Hnd) ? _Thrd_result::_Success : _Thrd_result::_Error;
}

void _Thrd_sleep(const _timespec64* xt) { // suspend thread until time xt
_CRTIMP2_PURE void __cdecl _Thrd_sleep(const _timespec64* xt) { // suspend thread until time xt
_timespec64 now;
_Timespec64_get_sys(&now);
do { // sleep and check time
Expand All @@ -81,35 +82,35 @@ void _Thrd_sleep(const _timespec64* xt) { // suspend thread until time xt
} while (now.tv_sec < xt->tv_sec || now.tv_sec == xt->tv_sec && now.tv_nsec < xt->tv_nsec);
}

void _Thrd_yield() { // surrender remainder of timeslice
_CRTIMP2_PURE void __cdecl _Thrd_yield() { // surrender remainder of timeslice
SwitchToThread();
}

// TRANSITION, ABI: _Thrd_equal() is preserved for binary compatibility
_CRTIMP2_PURE int _Thrd_equal(_Thrd_t thr0, _Thrd_t thr1) { // return 1 if thr0 and thr1 identify same thread
_CRTIMP2_PURE int __cdecl _Thrd_equal(_Thrd_t thr0, _Thrd_t thr1) { // return 1 if thr0 and thr1 identify same thread
return thr0._Id == thr1._Id;
}

// TRANSITION, ABI: _Thrd_current() is preserved for binary compatibility
_CRTIMP2_PURE _Thrd_t _Thrd_current() { // return _Thrd_t identifying current thread
_CRTIMP2_PURE _Thrd_t __cdecl _Thrd_current() { // return _Thrd_t identifying current thread
_Thrd_t result;
result._Hnd = nullptr;
result._Id = GetCurrentThreadId();
return result;
}

_Thrd_id_t _Thrd_id() { // return unique id for current thread
_CRTIMP2_PURE _Thrd_id_t __cdecl _Thrd_id() { // return unique id for current thread
return GetCurrentThreadId();
}

unsigned int _Thrd_hardware_concurrency() { // return number of processors
_CRTIMP2_PURE unsigned int __cdecl _Thrd_hardware_concurrency() { // return number of processors
SYSTEM_INFO info;
GetNativeSystemInfo(&info);
return info.dwNumberOfProcessors;
}

// TRANSITION, ABI: _Thrd_create() is preserved for binary compatibility
_CRTIMP2_PURE _Thrd_result _Thrd_create(_Thrd_t* thr, _Thrd_start_t func, void* d) { // create thread
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_create(_Thrd_t* thr, _Thrd_start_t func, void* d) { // create thread
_Thrd_result res;
_Thrd_binder b;
int started = 0;
Expand Down
75 changes: 39 additions & 36 deletions stl/src/mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

#include "primitives.hpp"

extern "C" [[noreturn]] _CRTIMP2_PURE void _Thrd_abort(const char* msg) { // abort on precondition failure
_EXTERN_C

[[noreturn]] _CRTIMP2_PURE void __cdecl _Thrd_abort(const char* msg) { // abort on precondition failure
fputs(msg, stderr);
fputc('\n', stderr);
abort();
Expand All @@ -29,26 +31,26 @@ extern "C" [[noreturn]] _CRTIMP2_PURE void _Thrd_abort(const char* msg) { // abo

// TRANSITION, ABI: preserved for binary compatibility
enum class __stl_sync_api_modes_enum { normal, win7, vista, concrt };
extern "C" _CRTIMP2 void __cdecl __set_stl_sync_api_mode(__stl_sync_api_modes_enum) {}
_CRTIMP2 void __cdecl __set_stl_sync_api_mode(__stl_sync_api_modes_enum) {}

[[nodiscard]] static PSRWLOCK get_srw_lock(_Mtx_t mtx) {
return reinterpret_cast<PSRWLOCK>(&mtx->_Critical_section._M_srw_lock);
}

// TRANSITION, only used when constexpr mutex constructor is not enabled
void _Mtx_init_in_situ(_Mtx_t mtx, int type) { // initialize mutex in situ
_CRTIMP2_PURE void __cdecl _Mtx_init_in_situ(_Mtx_t mtx, int type) { // initialize mutex in situ
Concurrency::details::create_stl_critical_section(&mtx->_Critical_section);
mtx->_Thread_id = -1;
mtx->_Type = type;
mtx->_Count = 0;
}

void _Mtx_destroy_in_situ(_Mtx_t mtx) { // destroy mutex in situ
_CRTIMP2_PURE void __cdecl _Mtx_destroy_in_situ(_Mtx_t mtx) { // destroy mutex in situ
_THREAD_ASSERT(mtx->_Count == 0, "mutex destroyed while busy");
(void) mtx;
}

_Thrd_result _Mtx_init(_Mtx_t* mtx, int type) { // initialize mutex
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_init(_Mtx_t* mtx, int type) { // initialize mutex
*mtx = nullptr;

_Mtx_t mutex = static_cast<_Mtx_t>(_calloc_crt(1, sizeof(_Mtx_internal_imp_t)));
Expand All @@ -63,90 +65,87 @@ _Thrd_result _Mtx_init(_Mtx_t* mtx, int type) { // initialize mutex
return _Thrd_result::_Success;
}

void _Mtx_destroy(_Mtx_t mtx) { // destroy mutex
_CRTIMP2_PURE void __cdecl _Mtx_destroy(_Mtx_t mtx) { // destroy mutex
if (mtx) { // something to do, do it
_Mtx_destroy_in_situ(mtx);
_free_crt(mtx);
}
}

static _Thrd_result mtx_do_lock(_Mtx_t mtx, const _timespec64* target) { // lock mutex
// TRANSITION, ABI: the use of `const _timespec64*` is preserved for `_Mtx_timedlock`
const auto current_thread_id = static_cast<long>(GetCurrentThreadId());
if ((mtx->_Type & ~_Mtx_recursive) == _Mtx_plain) { // set the lock
if (mtx->_Thread_id != static_cast<long>(GetCurrentThreadId())) { // not current thread, do lock
// TRANSITION, ABI: this branch is preserved for `_Thrd_create`

if (mtx->_Thread_id != current_thread_id) { // not current thread, do lock
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
AcquireSRWLockExclusive(get_srw_lock(mtx));
mtx->_Thread_id = static_cast<long>(GetCurrentThreadId());
mtx->_Thread_id = current_thread_id;
}
++mtx->_Count;

return _Thrd_result::_Success;
} else { // handle timed or recursive mutex
int res = WAIT_TIMEOUT;
if (target == nullptr) { // no target --> plain wait (i.e. infinite timeout)
if (mtx->_Thread_id != static_cast<long>(GetCurrentThreadId())) {
if (mtx->_Thread_id != current_thread_id) {
AcquireSRWLockExclusive(get_srw_lock(mtx));
}

res = WAIT_OBJECT_0;

} else if (target->tv_sec < 0 || target->tv_sec == 0 && target->tv_nsec <= 0) {
// target time <= 0 --> plain trylock or timed wait for time that has passed; try to lock with 0 timeout
if (mtx->_Thread_id != static_cast<long>(GetCurrentThreadId())) { // not this thread, lock it
if (mtx->_Thread_id != current_thread_id) { // not this thread, lock it
if (TryAcquireSRWLockExclusive(get_srw_lock(mtx)) != 0) {
res = WAIT_OBJECT_0;
} else {
res = WAIT_TIMEOUT;
}
} else {
res = WAIT_OBJECT_0;
}

} else { // check timeout
// TRANSITION, ABI: this branch is preserved for `_Mtx_timedlock`
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
_timespec64 now;
_Timespec64_get_sys(&now);
while (now.tv_sec < target->tv_sec || now.tv_sec == target->tv_sec && now.tv_nsec < target->tv_nsec) {
// time has not expired
if (mtx->_Thread_id == static_cast<long>(GetCurrentThreadId())
if (mtx->_Thread_id == current_thread_id
|| TryAcquireSRWLockExclusive(get_srw_lock(mtx)) != 0) { // stop waiting
res = WAIT_OBJECT_0;
break;
} else {
res = WAIT_TIMEOUT;
}

_Timespec64_get_sys(&now);
}
}

if (res == WAIT_OBJECT_0 || res == WAIT_ABANDONED) {
if (res == WAIT_OBJECT_0) {
if (1 < ++mtx->_Count) { // check count
if ((mtx->_Type & _Mtx_recursive) != _Mtx_recursive) { // not recursive, fixup count
--mtx->_Count;
res = WAIT_TIMEOUT;
}
} else {
mtx->_Thread_id = static_cast<long>(GetCurrentThreadId());
mtx->_Thread_id = current_thread_id;
}
}

switch (res) {
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
if (res == WAIT_OBJECT_0) {
return _Thrd_result::_Success;
}

case WAIT_TIMEOUT:
if (target == nullptr || (target->tv_sec == 0 && target->tv_nsec == 0)) {
return _Thrd_result::_Busy;
} else {
return _Thrd_result::_Timedout;
}
// res is WAIT_TIMEOUT here

default:
return _Thrd_result::_Error;
if (target == nullptr || (target->tv_sec == 0 && target->tv_nsec == 0)) {
return _Thrd_result::_Busy;
}

return _Thrd_result::_Timedout;
}
}

_Thrd_result _Mtx_unlock(_Mtx_t mtx) { // unlock mutex
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_unlock(_Mtx_t mtx) { // unlock mutex
_THREAD_ASSERT(
1 <= mtx->_Count && mtx->_Thread_id == static_cast<long>(GetCurrentThreadId()), "unlock of unowned mutex");

Expand All @@ -160,44 +159,48 @@ _Thrd_result _Mtx_unlock(_Mtx_t mtx) { // unlock mutex
return _Thrd_result::_Success; // TRANSITION, ABI: Always succeeds
}

_Thrd_result _Mtx_lock(_Mtx_t mtx) { // lock mutex
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_lock(_Mtx_t mtx) { // lock mutex
return mtx_do_lock(mtx, nullptr);
}

_Thrd_result _Mtx_trylock(_Mtx_t mtx) { // attempt to lock try_mutex
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_trylock(_Mtx_t mtx) { // attempt to lock try_mutex
_timespec64 xt;
_THREAD_ASSERT((mtx->_Type & (_Mtx_try | _Mtx_timed)) != 0, "trylock not supported by mutex");
xt.tv_sec = 0;
xt.tv_nsec = 0;
return mtx_do_lock(mtx, &xt);
}

_Thrd_result _Mtx_timedlock(_Mtx_t mtx, const _timespec64* xt) { // attempt to lock timed mutex
// TRANSITION, ABI: preserved for binary compatibility
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_timedlock(_Mtx_t mtx, const _timespec64* xt) { // attempt to lock timed mutex
_Thrd_result res;

_THREAD_ASSERT((mtx->_Type & _Mtx_timed) != 0, "timedlock not supported by mutex");
res = mtx_do_lock(mtx, xt);
return res == _Thrd_result::_Busy ? _Thrd_result::_Timedout : res;
}

int _Mtx_current_owns(_Mtx_t mtx) { // test if current thread owns mutex
_CRTIMP2_PURE int __cdecl _Mtx_current_owns(_Mtx_t mtx) { // test if current thread owns mutex
return mtx->_Count != 0 && mtx->_Thread_id == static_cast<long>(GetCurrentThreadId());
}

void* _Mtx_getconcrtcs(_Mtx_t mtx) { // get internal cs impl
// TRANSITION, ABI: preserved for binary compatibility
_CRTIMP2_PURE void* __cdecl _Mtx_getconcrtcs(_Mtx_t mtx) { // get internal cs impl
return &mtx->_Critical_section;
}

void _Mtx_clear_owner(_Mtx_t mtx) { // set owner to nobody
_CRTIMP2_PURE void __cdecl _Mtx_clear_owner(_Mtx_t mtx) { // set owner to nobody
mtx->_Thread_id = -1;
--mtx->_Count;
}

void _Mtx_reset_owner(_Mtx_t mtx) { // set owner to current thread
_CRTIMP2_PURE void __cdecl _Mtx_reset_owner(_Mtx_t mtx) { // set owner to current thread
mtx->_Thread_id = static_cast<long>(GetCurrentThreadId());
++mtx->_Count;
}

_END_EXTERN_C

/*
* This file is derived from software bearing the following
* restrictions:
Expand Down
Loading