Skip to content

Commit

Permalink
Cleanups for mutex utilities (#3912)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
achabense and StephanTLavavej authored Aug 11, 2023
1 parent 036ea59 commit ee3df46
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 61 deletions.
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*);
_CRTIMP2_PURE _Thrd_result __cdecl _Mtx_unlock(_Mtx_t); // TRANSITION, ABI: Always succeeds

_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
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`
_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

0 comments on commit ee3df46

Please sign in to comment.