// Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include #include #include #include #include extern "C" { // TRANSITION, ABI: exported only for ABI compat [[noreturn]] _CRTIMP2_PURE void __cdecl _Thrd_abort(const char* msg) noexcept { // abort on precondition failure fputs(msg, stderr); fputc('\n', stderr); abort(); } #ifdef _DEBUG #define _THREAD_QUOTX(x) #x #define _THREAD_QUOT(x) _THREAD_QUOTX(x) #define _THREAD_ASSERT(expr, msg) ((expr) ? (void) 0 : _Thrd_abort(__FILE__ "(" _THREAD_QUOT(__LINE__) "): " msg)) #else // ^^^ defined(_DEBUG) / !defined(_DEBUG) vvv #define _THREAD_ASSERT(expr, msg) ((void) 0) #endif // ^^^ !defined(_DEBUG) ^^^ // TRANSITION, ABI: preserved for binary compatibility enum class __stl_sync_api_modes_enum { normal, win7, vista, concrt }; _CRTIMP2 void __cdecl __set_stl_sync_api_mode(__stl_sync_api_modes_enum) noexcept {} [[nodiscard]] static PSRWLOCK get_srw_lock(_Mtx_t mtx) noexcept { return reinterpret_cast(&mtx->_Critical_section._M_srw_lock); } // TRANSITION, ABI: preserved for binary compatibility (and _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) _CRTIMP2_PURE void __cdecl _Mtx_init_in_situ(_Mtx_t mtx, int type) noexcept { // initialize mutex in situ new (&mtx->_Critical_section) _Stl_critical_section; mtx->_Thread_id = -1; mtx->_Type = type; mtx->_Count = 0; } // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE void __cdecl _Mtx_destroy_in_situ(_Mtx_t mtx) noexcept { // destroy mutex in situ _THREAD_ASSERT(mtx->_Count == 0, "mutex destroyed while busy"); (void) mtx; } // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_init(_Mtx_t* mtx, int type) noexcept { // initialize mutex *mtx = nullptr; _Mtx_t mutex = static_cast<_Mtx_t>(_calloc_crt(1, sizeof(_Mtx_internal_imp_t))); if (mutex == nullptr) { return _Thrd_result::_Nomem; // report alloc failed } _Mtx_init_in_situ(mutex, type); *mtx = mutex; return _Thrd_result::_Success; } // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE void __cdecl _Mtx_destroy(_Mtx_t mtx) noexcept { // 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) noexcept { // lock mutex // TRANSITION, ABI: the use of `const _timespec64*` is preserved for `_Mtx_timedlock` const auto current_thread_id = static_cast(GetCurrentThreadId()); if ((mtx->_Type & ~_Mtx_recursive) == _Mtx_plain) { // set the 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 = 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 != 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 != current_thread_id) { // not this thread, lock it if (TryAcquireSRWLockExclusive(get_srw_lock(mtx)) != 0) { res = WAIT_OBJECT_0; } } 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 == current_thread_id || TryAcquireSRWLockExclusive(get_srw_lock(mtx)) != 0) { // stop waiting res = WAIT_OBJECT_0; break; } _Timespec64_get_sys(&now); } } 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 = current_thread_id; } } if (res == WAIT_OBJECT_0) { return _Thrd_result::_Success; } // res is WAIT_TIMEOUT here if (target == nullptr || (target->tv_sec == 0 && target->tv_nsec == 0)) { return _Thrd_result::_Busy; } return _Thrd_result::_Timedout; } } _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_unlock(_Mtx_t mtx) noexcept { // unlock mutex _THREAD_ASSERT(mtx->_Count > 0, "unlock of unowned mutex"); _THREAD_ASSERT( mtx->_Thread_id == static_cast(GetCurrentThreadId()), "unlock of mutex not owned by the current thread"); if (--mtx->_Count == 0) { // leave critical section mtx->_Thread_id = -1; auto srw_lock = get_srw_lock(mtx); _Analysis_assume_lock_held_(*srw_lock); ReleaseSRWLockExclusive(srw_lock); } return _Thrd_result::_Success; // TRANSITION, ABI: Always succeeds } _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_lock(_Mtx_t mtx) noexcept { // lock mutex return mtx_do_lock(mtx, nullptr); } _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_trylock(_Mtx_t mtx) noexcept { // 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); } // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE _Thrd_result __cdecl _Mtx_timedlock(_Mtx_t mtx, const _timespec64* xt) noexcept { // 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; } _CRTIMP2_PURE int __cdecl _Mtx_current_owns(_Mtx_t mtx) noexcept { // test if current thread owns mutex return mtx->_Count != 0 && mtx->_Thread_id == static_cast(GetCurrentThreadId()); } // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE void* __cdecl _Mtx_getconcrtcs(_Mtx_t mtx) noexcept { // get internal cs impl return &mtx->_Critical_section; } } // extern "C" /* * This file is derived from software bearing the following * restrictions: * * (c) Copyright William E. Kempf 2001 * * Permission to use, copy, modify, distribute and sell this * software and its documentation for any purpose is hereby * granted without fee, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation. William E. Kempf makes no representations * about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. */