From 245b5ddeb63d20f127a6013a289f36cad0fb237b Mon Sep 17 00:00:00 2001 From: mingkuang Date: Thu, 2 May 2024 18:36:16 +0800 Subject: [PATCH] =?UTF-8?q?Fea=20#61,=20=E6=96=B0=E5=A2=9EC11=20Threads?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=88VS17.8=E6=96=B0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 16 + Build.proj | 8 +- Sources/crt/vcruntime/ltl/threads.cpp | 901 ++++++++++++++++++ UnitTest/C11ThreadsUnitTest.cpp | 527 ++++++++++ UnitTest/UnitTest.vcxproj | 1 + UnitTest/UnitTest.vcxproj.filters | 3 + UnitTest/pch.cpp | 9 +- vcruntime.msvcrt/vcruntime.msvcrt.vcxproj | 3 + .../vcruntime.msvcrt.vcxproj.filters | 3 + vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj | 3 + .../vcruntime.ucrtbase.vcxproj.filters | 3 + 11 files changed, 1467 insertions(+), 10 deletions(-) create mode 100644 .editorconfig create mode 100644 Sources/crt/vcruntime/ltl/threads.cpp create mode 100644 UnitTest/C11ThreadsUnitTest.cpp diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a9c528c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: https://EditorConfig.org + + +# 代码文件尽可能的使用 UTF-8,因为英语字符占据大多数。 +# 数据文件比如 XML,json统一缩进 2,因为外部规范往往如此 + +[*] +end_of_line = crlf +insert_final_newline = true +charset = utf-8-bom +indent_style = space +indent_size = 4 + +[*.{vcxproj,xml}] +indent_style = space +indent_size = 2 diff --git a/Build.proj b/Build.proj index a98fad7..0c835f8 100644 --- a/Build.proj +++ b/Build.proj @@ -76,9 +76,9 @@ - - - + + + @@ -210,4 +210,4 @@ WorkingDirectory="$(MSBuildThisFileDirectory)Tools\$(WindowsTargetPlatformMinVersion)\$(Platform)" Condition="Exists('$(MSBuildThisFileDirectory)Tools\$(WindowsTargetPlatformMinVersion)\$(Platform)\Build_libucrt_shared.cmd')"/> - \ No newline at end of file + diff --git a/Sources/crt/vcruntime/ltl/threads.cpp b/Sources/crt/vcruntime/ltl/threads.cpp new file mode 100644 index 0000000..fbd366c --- /dev/null +++ b/Sources/crt/vcruntime/ltl/threads.cpp @@ -0,0 +1,901 @@ +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "framework.h" + +// 模仿 vctools\crt\vcruntime\src\internal\threads.cpp 实现 + +#pragma warning(disable: 28251) + +namespace VC_LTL +{ + constexpr auto kDtorTableBufferLength = 0x400; + + class srwlock_shared_guard + { + private: + SRWLOCK* pLock; + + public: + srwlock_shared_guard(SRWLOCK& _Lock) + : pLock(&_Lock) + { + AcquireSRWLockShared(pLock); + } + + srwlock_shared_guard(const srwlock_shared_guard&) = delete; + + ~srwlock_shared_guard() + { + Reset(); + } + + void Reset() + { + if (pLock) + { + ReleaseSRWLockShared(pLock); + pLock = nullptr; + } + } + + void Attach(SRWLOCK* _pLock) + { + if (pLock == _pLock) + return; + + if (pLock) + ReleaseSRWLockShared(pLock); + + pLock = _pLock; + AcquireSRWLockShared(pLock); + } + + void operator=(std::nullptr_t) + { + Reset(); + } + + void operator=(SRWLOCK& _Lock) + { + Attach(&_Lock); + } + }; + + class srwlock_guard + { + private: + SRWLOCK* pLock; + + public: + srwlock_guard(SRWLOCK& _Lock) + : pLock(&_Lock) + { + AcquireSRWLockExclusive(pLock); + } + + srwlock_guard(const srwlock_guard&) = delete; + + ~srwlock_guard() + { + ReleaseSRWLockExclusive(pLock); + } + }; + + struct tss_ptd + { + tss_ptd* next; + tss_ptd* prev; + void* data[kDtorTableBufferLength]; + bool tss_dtor_used; + }; + + class tss_global_data_t + { + private: + // 0 + SRWLOCK lock = SRWLOCK_INIT; + // 0x4 + DWORD tss_ptd_idx = TLS_OUT_OF_INDEXES; + // 0x8 + tss_dtor_t* dtor_table = nullptr; + // 0xC + tss_ptd* ptd_list = nullptr; + // 0x10 + uint32_t last_idx = 0; + + public: + constexpr tss_global_data_t() = default; + + tss_global_data_t(const tss_global_data_t&) = delete; + + int create(tss_t* _Key, tss_dtor_t _Dtor) + { + if (!_Dtor) + _Dtor = noop_dtor; + + int _iResult = thrd_success; + AcquireSRWLockExclusive(&lock); + do + { + if (!dtor_table) + { + dtor_table = static_cast(calloc(kDtorTableBufferLength, sizeof(tss_dtor_t))); + if (!dtor_table) + { + _iResult = thrd_error; + break; + } + } + + if (tss_ptd_idx == TLS_OUT_OF_INDEXES) + { + tss_ptd_idx = TlsAlloc(); + if (tss_ptd_idx == TLS_OUT_OF_INDEXES) + { + free(dtor_table); + dtor_table = nullptr; + _iResult = thrd_error; + break; + } + } + + for (; dtor_table[last_idx];) + { + last_idx = (last_idx + 1) % kDtorTableBufferLength; + if (last_idx == 0) + { + // dtor_table已经满了 + _iResult = thrd_error; + break; + } + } + + dtor_table[last_idx] = _Dtor; + _Key->_Idx = last_idx; + } while (false); + ReleaseSRWLockExclusive(&lock); + return _iResult; + } + + void tss_delete(tss_t _Key) + { + AcquireSRWLockExclusive(&lock); + + _ASSERT_EXPR(dtor_table, "tss dtor table not allocated."); + _ASSERT_EXPR(dtor_table[_Key._Idx] != nullptr, "tss dtor table indicates delete called on nonexistant tss index."); + + dtor_table[_Key._Idx] = nullptr; + + for (auto _pItem = ptd_list; _pItem; _pItem = _pItem->next) + { + _pItem->data[_Key._Idx] = nullptr; + } + + ReleaseSRWLockExclusive(&lock); + } + + void* get(tss_t _Key) + { + void* _pValue = nullptr; + + AcquireSRWLockShared(&lock); + _ASSERT_EXPR(dtor_table, "tss dtor table not allocated."); + _ASSERT_EXPR(dtor_table[_Key._Idx] != nullptr, "tss dtor table indicates delete called on nonexistant tss index."); + + if (tss_ptd_idx != TLS_OUT_OF_INDEXES) + { + auto _pTssData = (tss_ptd*)TlsGetValue(tss_ptd_idx); + if (_pTssData) + { + _pValue = _pTssData->data[_Key._Idx]; + } + } + + ReleaseSRWLockShared(&lock); + return _pValue; + } + + int set(tss_t _Key, void* _Val) + { + { + srwlock_shared_guard _AutoSharedLock(lock); + _ASSERT_EXPR(tss_ptd_idx != TLS_OUT_OF_INDEXES, "tss_set called but the tss ptd index was never allcoated."); + + auto _pTssData = (tss_ptd*)TlsGetValue(tss_ptd_idx); + _ASSERT_EXPR(GetLastError() == ERROR_SUCCESS, "tss_set could not get the value of the tss ptd."); + + if (_pTssData) + { + if (dtor_table[_Key._Idx] != noop_dtor) + { + _pTssData->tss_dtor_used = true; + } + + _pTssData->data[_Key._Idx] = _Val; + return thrd_success; + } + + if (!_Val) + { + return thrd_success; + } + } + + auto _pTssData = (tss_ptd*)calloc(1, sizeof(tss_ptd)); + if (!_pTssData) + return thrd_error; + + TlsSetValue(tss_ptd_idx, _pTssData); + { + srwlock_guard _AutoExclusiveLock(lock); + + _pTssData->next = ptd_list; + if (ptd_list) + { + ptd_list->prev = _pTssData; + } + ptd_list = _pTssData; + } + + srwlock_shared_guard _AutoSharedLock(lock); + if (dtor_table[_Key._Idx] != noop_dtor) + { + _pTssData->tss_dtor_used = true; + } + + _pTssData->data[_Key._Idx] = _Val; + return thrd_success; + } + + void cleanup(void) + { + tss_ptd* ptd = nullptr; + { + srwlock_shared_guard _AutoLock(lock); + if (!dtor_table) + { + return; + } + + _ASSERTE((tss_ptd_idx != TLS_OUT_OF_INDEXES, "cleanup called but the tss ptd index was never allcoated.")); + + ptd = (tss_ptd*)TlsGetValue(tss_ptd_idx); + _ASSERTE((GetLastError() == ERROR_SUCCESS, "cleanup could not get the value of the tss ptd.")); + if (!ptd) + { + return; + } + + if (ptd->tss_dtor_used) + { + for (int i = 0; i != kDtorTableBufferLength; ++i) + { + auto _pFun = dtor_table[i]; + if (_pFun && _pFun != &tss_global_data_t::noop_dtor) + { + auto _pUserData = ptd->data[i]; + ptd->data[i] = nullptr; + _AutoLock = nullptr; + _pFun(_pUserData); + _AutoLock = lock; + } + } + } + } + + srwlock_guard AutoLock(lock); + if (ptd->prev) + { + ptd->prev = ptd->next; + } + else + { + _ASSERTE((ptd_list == ptd, "ptd list is malformed.")); + ptd_list = ptd->next; + } + if (ptd->next) + { + ptd->next->prev = ptd->next; + } + free(ptd); + } + + void cleanup_global(void) + { + if (dtor_table) + { + free(dtor_table); + dtor_table = reinterpret_cast(-1); + } + + if (tss_ptd_idx != TLS_OUT_OF_INDEXES) + { + const auto _bSuccess = TlsFree(tss_ptd_idx); + tss_ptd_idx = TLS_OUT_OF_INDEXES - 1; + _ASSERTE((_bSuccess, "failed to free ptd index.")); + } + } + + private: + static void __cdecl noop_dtor(void*) + { + // 什么也不用做 + } + }; +} + +using namespace VC_LTL; + +extern "C" void __cdecl mtx_destroy(mtx_t * _Mtx) +{ + // 什么也不做 +} + +_LCRT_DEFINE_IAT_SYMBOL(mtx_destroy); + +extern "C" int __cdecl mtx_init(_Out_ mtx_t* _Mtx, int _Type) +{ + _Mtx->_Type = _Type; + _Mtx->_Ptr = nullptr; + _Mtx->_Cv = nullptr; + _Mtx->_Owner = 0; + _Mtx->_Cnt = 0; + return thrd_success; +} + +_LCRT_DEFINE_IAT_SYMBOL(mtx_init); + +static int __cdecl mtx_dolock(mtx_t * _Mtx) +{ + const auto _uCurrentThreadId = GetCurrentThreadId(); + if (_uCurrentThreadId == _Mtx->_Owner) + { + if ((_Mtx->_Type & mtx_recursive) == 0) + { + abort(); + } + _Mtx->_Cnt++; + return thrd_success; + } + + for(; _Mtx->_Owner;) + { + if (!SleepConditionVariableSRW( + reinterpret_cast(&_Mtx->_Cv), + reinterpret_cast(&_Mtx->_Ptr), + INFINITE, 0)) + { + return thrd_error; + } + } + + _Mtx->_Owner = _uCurrentThreadId; + _Mtx->_Cnt = 1; + return thrd_success; +} + +extern "C" int __cdecl mtx_lock(mtx_t* _Mtx) +{ + AcquireSRWLockExclusive(reinterpret_cast(& _Mtx->_Ptr)); + auto _iResult = mtx_dolock(_Mtx); + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +_LCRT_DEFINE_IAT_SYMBOL(mtx_lock); + +template +static int64_t __fastcall duration_timespec_to_ms(const timespec& _Duration) +{ + return _Duration.tv_sec * 1000ll + _Duration.tv_nsec / 1000000; +} + +static int64_t __fastcall abstime_timespec_to_duration_ms(const struct _timespec32* __restrict _Ts) +{ + _timespec32 _Now; + if (_timespec32_get(&_Now, TIME_UTC) == 0) + { + return INT64_MAX; + } + + _Now.tv_sec = _Ts->tv_sec - _Now.tv_sec; + _Now.tv_nsec = _Ts->tv_nsec - _Now.tv_nsec; + return duration_timespec_to_ms(_Now); +} + +static int64_t __fastcall abstime_timespec_to_duration_ms(const struct _timespec64* __restrict _Ts) +{ + _timespec64 _Now; + if (_timespec64_get(&_Now, TIME_UTC) == 0) + { + return INT64_MAX; + } + + _Now.tv_sec = _Ts->tv_sec - _Now.tv_sec; + _Now.tv_nsec = _Ts->tv_nsec - _Now.tv_nsec; + return duration_timespec_to_ms(_Now); +} + +template +static int __cdecl common_mtx_timedlock( + mtx_t* __restrict _Mtx, const timespec* __restrict _Ts) +{ + int _iResult = 0; + const auto _uCurrentThreadId = GetCurrentThreadId(); + + AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + + do + { + const auto _uCurrentThreadId = GetCurrentThreadId(); + if (_uCurrentThreadId == _Mtx->_Owner) + { + if ((_Mtx->_Type & mtx_recursive) == 0) + { + abort(); + } + _Mtx->_Cnt++; + _iResult = thrd_success; + break; + } + + for (;;) + { + if (_Mtx->_Owner == 0) + { + _Mtx->_Owner = _uCurrentThreadId; + _Mtx->_Cnt = 1; + _iResult = thrd_success; + break; + } + const auto _iDuration = abstime_timespec_to_duration_ms(_Ts); + if (_iDuration < 0ll) + { + _iResult = thrd_timedout; + break; + } + else if (_iDuration > int64_t(UINT32_MAX)) + { + _iResult = thrd_error; + break; + } + + if (SleepConditionVariableSRW( + reinterpret_cast(&_Mtx->_Cv), + reinterpret_cast(&_Mtx->_Ptr), + static_cast(_iDuration), 0)) + { + continue; + } + + if (GetLastError() != ERROR_TIMEOUT) + { + _iResult = thrd_error; + break; + } + } + + break; + } while (false); + + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +extern "C" int __cdecl _mtx_timedlock32( + mtx_t * __restrict _Mtx, const struct _timespec32* __restrict _Ts) +{ + return common_mtx_timedlock(_Mtx, _Ts); +} + +_LCRT_DEFINE_IAT_SYMBOL(_mtx_timedlock32); + +extern "C" int __cdecl _mtx_timedlock64( + mtx_t* __restrict _Mtx, const struct _timespec64* __restrict _Ts) +{ + return common_mtx_timedlock(_Mtx, _Ts); +} + +_LCRT_DEFINE_IAT_SYMBOL(_mtx_timedlock64); + +extern "C" int __cdecl mtx_trylock(mtx_t* _Mtx) +{ + int _iResult = thrd_success; + const auto _uCurrentThreadId = GetCurrentThreadId(); + + AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + + if (_Mtx->_Owner == 0) + { + _Mtx->_Owner = _uCurrentThreadId; + _Mtx->_Cnt = 1; + _iResult = thrd_success; + } + else if (_uCurrentThreadId == _Mtx->_Owner) + { + if ((_Mtx->_Type & mtx_recursive) == 0) + { + _iResult = thrd_busy; + } + else + { + _Mtx->_Cnt++; + _iResult = thrd_success; + } + } + else + { + _iResult = thrd_busy; + } + + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +_LCRT_DEFINE_IAT_SYMBOL(mtx_trylock); + +static int __cdecl mtx_dounlock(mtx_t* _Mtx) +{ + if (GetCurrentThreadId() != _Mtx->_Owner) + { + abort(); + } + + if (--(_Mtx->_Cnt) == 0) + { + _Mtx->_Owner = 0; + } + WakeConditionVariable(reinterpret_cast(&_Mtx->_Cv)); + return thrd_success; +} + +extern "C" int __cdecl mtx_unlock(mtx_t* _Mtx) +{ + AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + const auto _iResult = mtx_dounlock(_Mtx); + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +_LCRT_DEFINE_IAT_SYMBOL(mtx_unlock); + +extern "C" int __cdecl cnd_broadcast(cnd_t * _Cond) +{ + WakeAllConditionVariable(reinterpret_cast(&_Cond->_Ptr)); + return thrd_success; +} + +_LCRT_DEFINE_IAT_SYMBOL(cnd_broadcast); + +extern "C" void __cdecl cnd_destroy(cnd_t * _Cond) +{ + // 什么也不做 +} + +_LCRT_DEFINE_IAT_SYMBOL(cnd_destroy); + +extern "C" int __cdecl cnd_init(cnd_t * _Cond) +{ + InitializeConditionVariable(reinterpret_cast(&_Cond->_Ptr)); + return thrd_success; +} + +_LCRT_DEFINE_IAT_SYMBOL(cnd_init); + +extern "C" int __cdecl cnd_signal(cnd_t * _Cond) +{ + WakeConditionVariable(reinterpret_cast(&_Cond->_Ptr)); + return thrd_success; +} + +_LCRT_DEFINE_IAT_SYMBOL(cnd_signal); + +template +static int __cdecl common_cnd_timedwait(cnd_t* _Cond, mtx_t* _Mtx, const timespec* _Ts) +{ + int _iResult = thrd_success; + AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + + do + { + if (mtx_dounlock(_Mtx) != thrd_success) + { + _iResult = thrd_error; + break; + } + + for (;;) + { + const auto _iDuration = abstime_timespec_to_duration_ms(_Ts); + if (_iDuration <= 0ll) + { + _iResult = thrd_timedout; + break; + } + else if (_iDuration > int64_t(UINT32_MAX)) + { + _iResult = thrd_error; + break; + } + + if (SleepConditionVariableSRW(reinterpret_cast(&_Cond->_Ptr), reinterpret_cast(&_Mtx->_Ptr), static_cast(_iDuration), 0)) + { + _iResult = thrd_success; + break; + } + } + + if (mtx_dolock(_Mtx) != thrd_success) + { + _iResult = thrd_error; + break; + } + } while (false); + + + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +extern "C" int __cdecl _cnd_timedwait32(cnd_t * _Cond, mtx_t * _Mtx, const struct _timespec32* _Ts) +{ + return common_cnd_timedwait(_Cond, _Mtx, _Ts); +} + +_LCRT_DEFINE_IAT_SYMBOL(_cnd_timedwait32); + +extern "C" int __cdecl _cnd_timedwait64(cnd_t* _Cond, mtx_t* _Mtx, const struct _timespec64* _Ts) +{ + return common_cnd_timedwait(_Cond, _Mtx, _Ts); +} + +_LCRT_DEFINE_IAT_SYMBOL(_cnd_timedwait64); + +extern "C" int cnd_wait(cnd_t* _Cond, mtx_t* _Mtx) +{ + int _iResult = thrd_success; + AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + + do + { + if (mtx_dounlock(_Mtx) != thrd_success) + { + _iResult = thrd_error; + break; + } + + if (!SleepConditionVariableSRW(reinterpret_cast(&_Cond->_Ptr), reinterpret_cast(&_Mtx->_Ptr), INFINITE, 0)) + { + _iResult = thrd_error; + } + + if (mtx_dolock(_Mtx) != thrd_success) + { + _iResult = thrd_error; + break; + } + } while (false); + + ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr)); + return _iResult; +} + +_LCRT_DEFINE_IAT_SYMBOL(cnd_wait); + +extern "C" int __cdecl thrd_create(thrd_t * _Thr, thrd_start_t _Func, void* _Arg) +{ +#if defined(_X86_) + // x86平台 __cdecl 与 __stdcall是不同的调用约定 + struct thrd_start_data + { + thrd_start_t Func; + void* Arg; + }; + + auto _pThreadData = (thrd_start_data*)malloc(sizeof(thrd_start_data)); + if (!_pThreadData) + return thrd_nomem; + + _pThreadData->Func = _Func; + _pThreadData->Arg = _Arg; + + errno = 0; + _Thr->_Handle = reinterpret_cast(_beginthreadex(nullptr, 0, + [](void* _pUserData) -> unsigned + { + auto _pStartData = static_cast(_pUserData); + auto _uResult = _pStartData->Func(_pStartData->Arg); + free(_pStartData); + return _uResult; + }, _pThreadData, 0, &_Thr->_Tid)); + + if (_Thr->_Handle) + { + return thrd_success; + } + + // 微软原版存在一点瑕疵,应该先取错误代码,然后再释放内存 + // 避免free时 errno 修改风险。 + const auto _iResult = (errno == 0 || errno == ENOMEM) ? thrd_nomem : thrd_error; + free(_pThreadData); + return _iResult; +#else + // 其他平台 __cdecl 与 __stdcall等价,所以不用额外跳转直接就可以用。 + errno = 0; + _Thr->_Handle = reinterpret_cast(_beginthreadex(nullptr, 0, reinterpret_cast<_beginthreadex_proc_type>(_Func), _Arg, 0, &_Thr->_Tid)); + if (_Thr->_Handle) + { + return thrd_success; + } + + return (errno == 0 || errno == ENOMEM) ? thrd_nomem : thrd_error; +#endif +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_create); + +extern "C" thrd_t __cdecl thrd_current(void) +{ + thrd_t _Result; + _Result._Handle = nullptr; + _Result._Tid = GetCurrentThreadId(); + return _Result; +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_current); + +extern "C" int __cdecl thrd_detach(thrd_t _Thr) +{ + return CloseHandle(_Thr._Handle) ? thrd_success : thrd_error; +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_detach); + +extern "C" int __cdecl thrd_equal(thrd_t _Thr0, thrd_t _Thr1) +{ + return _Thr0._Tid == _Thr1._Tid; +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_equal); + +extern "C" void __cdecl thrd_exit(int _Res) +{ + ExitThread(static_cast(_Res)); +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_exit); + +extern "C" int __cdecl thrd_join(thrd_t _Thr, int* _Res) +{ + DWORD _uExitCode; + if (WaitForSingleObject(_Thr._Handle, INFINITE) != /*WAIT_OBJECT_0*/ 0 || GetExitCodeThread(_Thr._Handle, &_uExitCode) == FALSE) + { + return thrd_error; + } + + if (_Res) + { + *_Res = static_cast(_uExitCode); + } + return CloseHandle(_Thr._Handle) ? thrd_success : thrd_error; +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_join); + +extern "C" int __cdecl _thrd_sleep32( + const struct _timespec32* duration, struct _timespec32* remaining) +{ + const auto _iDuration = duration_timespec_to_ms(*duration); + if (_iDuration >= 0ll && _iDuration <= int64_t(UINT32_MAX)) + { + Sleep(static_cast(_iDuration)); + return 0; + } + else + { + return -2; + } +} + +_LCRT_DEFINE_IAT_SYMBOL(_thrd_sleep32); + +extern "C" int __cdecl _thrd_sleep64( + const struct _timespec64* duration, struct _timespec64* remaining) +{ + const auto _iDuration = duration_timespec_to_ms(*duration); + if (_iDuration >= 0ll && _iDuration <= int64_t(UINT32_MAX)) + { + Sleep(static_cast(_iDuration)); + return 0; + } + else + { + return -2; + } +} + +_LCRT_DEFINE_IAT_SYMBOL(_thrd_sleep64); + +extern "C" void __cdecl thrd_yield(void) +{ + SwitchToThread(); +} + +_LCRT_DEFINE_IAT_SYMBOL(thrd_yield); + +static tss_global_data_t tss_global_data; + +extern "C" int __cdecl tss_create(tss_t * _Key, tss_dtor_t _Dtor) +{ + return tss_global_data.create(_Key, _Dtor); +} + +_LCRT_DEFINE_IAT_SYMBOL(tss_create); + +extern "C" void __cdecl tss_delete(tss_t _Key) +{ + tss_global_data.tss_delete(_Key); +} + +_LCRT_DEFINE_IAT_SYMBOL(tss_delete); + +extern "C" void* __cdecl tss_get(tss_t _Key) +{ + return tss_global_data.get(_Key); +} + +_LCRT_DEFINE_IAT_SYMBOL(tss_get); + +extern "C" int __cdecl tss_set(tss_t _Key, void* _Val) +{ + return tss_global_data.set(_Key, _Val); +} + +_LCRT_DEFINE_IAT_SYMBOL(tss_set); + +extern "C" void __cdecl call_once(once_flag* _Flag, void(*_Func)(void)) +{ + InitOnceExecuteOnce(reinterpret_cast(_Flag), + [](_In_ PINIT_ONCE InitOnce, + _In_ PVOID Parameter, + _In_opt_ PVOID* Context) ->BOOL + { + auto _Func = static_cast(Parameter); + _Func(); + return TRUE; + }, + _Func, + nullptr); +} + +_LCRT_DEFINE_IAT_SYMBOL(call_once); + +#pragma section(".CRT$XLE", long, read) + +static void NTAPI ClearTss( + PVOID DllHandle, + DWORD Reason, + PVOID Reserved) +{ + switch (Reason) + { + case DLL_PROCESS_DETACH: + tss_global_data.cleanup_global(); + break; + case DLL_THREAD_DETACH: + tss_global_data.cleanup(); + break; + } +} + +static _CRTALLOC(".CRT$XLE") PIMAGE_TLS_CALLBACK volmd = &ClearTss; diff --git a/UnitTest/C11ThreadsUnitTest.cpp b/UnitTest/C11ThreadsUnitTest.cpp new file mode 100644 index 0000000..42e6b15 --- /dev/null +++ b/UnitTest/C11ThreadsUnitTest.cpp @@ -0,0 +1,527 @@ +#include "pch.h" +#include "CppUnitTest.h" + +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace C11Threads +{ + constexpr auto kReturnCode = 8848; + + TEST_CLASS(Threads) + { + public: + TEST_METHOD(thrd_create与thrd_detach) + { + thrd_t _Thrd; + auto _iResult = ::thrd_create(&_Thrd, [](void* _pUserData) -> int + { + return kReturnCode; + }, nullptr); + + Assert::AreEqual(_iResult, 0); + + _iResult = thrd_detach(_Thrd); + Assert::AreEqual(_iResult, 0); + } + + TEST_METHOD(thrd_current) + { + auto _Result = ::thrd_current(); + Assert::AreEqual(_Result._Tid, static_cast(GetCurrentThreadId())); + } + + TEST_METHOD(thrd_join) + { + int _iOut = 0; + thrd_t _Thrd; + auto _iResult = ::thrd_create(&_Thrd, [](void* _pUserData) -> int + { + *(int*)_pUserData = kReturnCode; + Sleep(500); + return kReturnCode; + }, &_iOut); + Assert::AreEqual(_iResult, 0); + + int _iExitCode = 0; + _iResult = ::thrd_join(_Thrd, &_iExitCode); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(_iExitCode, kReturnCode); + Assert::AreEqual(_iOut, kReturnCode); + } + + TEST_METHOD(thrd_equal) + { + { + thrd_t _Left = { reinterpret_cast(888), 111 }; + thrd_t _Right = { reinterpret_cast(5), 111 };; + + auto _bResult = ::thrd_equal(_Left, _Right); + Assert::IsTrue(_bResult); + } + + { + thrd_t _Left = { reinterpret_cast(888), 111 }; + thrd_t _Right = { reinterpret_cast(888), 22 };; + + auto _bResult = ::thrd_equal(_Left, _Right); + Assert::IsFalse(_bResult); + } + } + + TEST_METHOD(thrd_exit) + { + thrd_t _Thrd; + auto _iResult = ::thrd_create(&_Thrd, [](void*) -> int + { + ::thrd_exit(kReturnCode); + return 1; + }, nullptr); + Assert::AreEqual(_iResult, 0); + + int _iExitCode = 0; + _iResult = ::thrd_join(_Thrd, &_iExitCode); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(_iExitCode, kReturnCode); + } + + TEST_METHOD(_thrd_sleep64) + { + // 等待500ms + { + _timespec64 _Duration = { 0, 1000000 * 500 }; + + const auto _uStart = GetTickCount64(); + auto _iResult = ::_thrd_sleep64(&_Duration, nullptr); + const auto _uTick = GetTickCount64() - _uStart; + Assert::AreEqual(_iResult, 0); + Assert::IsTrue(_uTick >= 400 && _uTick <= 700); + } + + // 等待 1.5秒 + { + _timespec64 _Duration = { 1, 1000000 * 500 }; + + const auto _uStart = GetTickCount64(); + auto _iResult = ::_thrd_sleep64(&_Duration, nullptr); + const auto _uTick = GetTickCount64() - _uStart; + Assert::AreEqual(_iResult, 0); + Assert::IsTrue(_uTick >= 1400 && _uTick <= 1700); + } + + // 超大等待时间,返回失败 + { + _timespec64 _Duration = { UINT32_MAX, 0 }; + auto _iResult = ::_thrd_sleep64(&_Duration, nullptr); + Assert::AreNotEqual(_iResult, 0); + } + } + }; + + static void* g_pClearupData = 0; + + TEST_CLASS(ThreadSpecificStorage) + { + public: + TEST_METHOD(tss_create与tss_delete) + { + tss_t _Key; + auto _iResult = ::tss_create(&_Key, nullptr); + Assert::AreEqual(_iResult, 0); + + ::tss_delete(_Key); + } + + TEST_METHOD(Tss清理机制) + { + g_pClearupData = nullptr; + tss_t _Key; + auto _iResult = ::tss_create(&_Key, [](void* _pData) + { + g_pClearupData = _pData; + }); + Assert::AreEqual(_iResult, 0); + + auto _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + auto& _Key = *static_cast(_pUserData); + auto _iResult = ::tss_set(_Key, (void*)7); + Assert::AreEqual(_iResult, 0); + return 0; + }, &_Key, 0, nullptr); + + Assert::IsNotNull(_hThread); + + Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0); + + CloseHandle(_hThread); + + Assert::AreEqual(g_pClearupData, (void*)7); + + ::tss_delete(_Key); + } + + TEST_METHOD(单线程tss_get与tss_set) + { + tss_t _Key; + auto _iResult = ::tss_create(&_Key, nullptr); + Assert::AreEqual(_iResult, 0); + + Assert::AreEqual(::tss_get(_Key), (void*)NULL); + + _iResult = ::tss_set(_Key, (void*)5); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(::tss_get(_Key), (void*)5); + + _iResult = ::tss_set(_Key, (void*)7); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(::tss_get(_Key), (void*)7); + + ::tss_delete(_Key); + } + + TEST_METHOD(多线程tss_get与tss_set) + { + tss_t _Key; + auto _iResult = ::tss_create(&_Key, nullptr); + _iResult = ::tss_set(_Key, (void*)5); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(::tss_get(_Key), (void*)5); + + + auto _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + auto& _Key = *static_cast(_pUserData); + Assert::AreEqual(::tss_get(_Key), (void*)0); + auto _iResult = ::tss_set(_Key, (void*)7); + Assert::AreEqual(_iResult, 0); + Assert::AreEqual(::tss_get(_Key), (void*)7); + return 0; + }, &_Key, 0, nullptr); + + Assert::IsNotNull(_hThread); + + Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0); + + CloseHandle(_hThread); + + Assert::AreEqual(::tss_get(_Key), (void*)5); + } + }; + + TEST_CLASS(Mutexes) + { + public: + TEST_METHOD(mtx_init) + { + { + mtx_t _Mtx; + auto _iResult = ::mtx_init(&_Mtx, 0); + Assert::AreEqual(_iResult, 0); + mtx_destroy(&_Mtx); + } + + { + mtx_t _Mtx; + auto _iResult = ::mtx_init(&_Mtx, mtx_recursive); + Assert::AreEqual(_iResult, 0); + mtx_destroy(&_Mtx); + } + } + + TEST_METHOD(mtx_lock与mtx_unlock) + { + // 锁定后其他线程会等待 + { + mtx_t _Mtx; + ::mtx_init(&_Mtx, 0); + + auto _iResult = ::mtx_lock(&_Mtx); + Assert::AreEqual(_iResult, 0); + + auto _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + auto& _Mtx = *static_cast(_pUserData); + + auto _uStart = GetTickCount64(); + auto _iResult = ::mtx_lock(&_Mtx); + auto _uTick = GetTickCount64() - _uStart; + + _iResult = ::mtx_unlock(&_Mtx); + Assert::AreEqual(_iResult, 0); + return static_cast(_uTick); + }, &_Mtx, 0, nullptr); + + Assert::IsNotNull(_hThread); + + Sleep(500); + _iResult = ::mtx_unlock(&_Mtx); + Assert::AreEqual(_iResult, 0); + + Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0); + + DWORD _uCode = 0; + GetExitCodeThread(_hThread, &_uCode); + CloseHandle(_hThread); + + Assert::IsTrue(_uCode <= 700 && _uCode >= 400); + } + + // 递归时多次加锁解锁不会有事情 + { + mtx_t _Mtx; + ::mtx_init(&_Mtx, mtx_recursive); + + auto _iResult = ::mtx_lock(&_Mtx); + Assert::AreEqual(_iResult, 0); + _iResult = ::mtx_lock(&_Mtx); + Assert::AreEqual(_iResult, 0); + _iResult = ::mtx_unlock(&_Mtx); + Assert::AreEqual(_iResult, 0); + _iResult = ::mtx_unlock(&_Mtx); + Assert::AreEqual(_iResult, 0); + } + } + + TEST_METHOD(mtx_trylock) + { + { + mtx_t _Mtx; + ::mtx_init(&_Mtx, 0); + + auto _iResult = ::mtx_trylock(&_Mtx); + Assert::AreEqual(_iResult, 0); + + _iResult = ::mtx_trylock(&_Mtx); + Assert::AreNotEqual(_iResult, 0); + + _iResult = ::mtx_unlock(&_Mtx); + Assert::AreEqual(_iResult, 0); + + _iResult = ::mtx_trylock(&_Mtx); + Assert::AreEqual(_iResult, 0); + } + + { + mtx_t _Mtx; + auto _iResult = ::mtx_init(&_Mtx, mtx_recursive); + Assert::AreEqual(_iResult, 0); + + _iResult = ::mtx_trylock(&_Mtx); + Assert::AreEqual(_iResult, 0); + + _iResult = ::mtx_trylock(&_Mtx); + Assert::AreEqual(_iResult, 0); + } + } + + TEST_METHOD(_mtx_timedlock64) + { + mtx_t _Mtx; + auto _iResult = ::mtx_init(&_Mtx, 0); + Assert::AreEqual(_iResult, 0); + + _timespec64 _Now; + _iResult = _timespec64_get(&_Now, TIME_UTC); + Assert::AreEqual(_iResult, TIME_UTC); + + _iResult = ::_mtx_timedlock64(&_Mtx, &_Now); + Assert::AreEqual(_iResult, 0); + + auto _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + auto& _Mtx = *static_cast(_pUserData); + auto _uStart = GetTickCount64(); + + _timespec64 _Now; + auto _iResult = _timespec64_get(&_Now, TIME_UTC); + Assert::AreEqual(_iResult, TIME_UTC); + _Now.tv_sec += 1; + + _iResult = ::_mtx_timedlock64(&_Mtx, &_Now); + Assert::AreEqual(_iResult, 2); + auto _uTick = GetTickCount64() - _uStart; + return static_cast(_uTick); + }, &_Mtx, 0, nullptr); + + Assert::IsNotNull(_hThread); + Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0); + + DWORD _uCode = 0; + GetExitCodeThread(_hThread, &_uCode); + CloseHandle(_hThread); + + Assert::IsTrue(_uCode <= 1200 && _uCode >= 900); + } + }; + + TEST_CLASS(ConditionVariables) + { + public: + TEST_METHOD(cnd_init) + { + cnd_t _Cond; + auto _iResult = ::cnd_init(&_Cond); + Assert::AreEqual(_iResult, 0); + } + + TEST_METHOD(cnd_signal与cnd_broadcast与cnd_wait) + { + struct ThreadData + { + mtx_t Mtx; + cnd_t Cond; + }; + + ThreadData _ThreadData; + + auto _iResult = ::mtx_init(&_ThreadData.Mtx, 0); + Assert::AreEqual(_iResult, 0); + + _iResult = ::cnd_init(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + + HANDLE _hThreads[5]; + for (auto& _hThread : _hThreads) + { + _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + auto& _ThreadData = *static_cast(_pUserData); + auto _iResult = ::mtx_lock(&_ThreadData.Mtx); + Assert::AreEqual(_iResult, 0); + ::cnd_wait(&_ThreadData.Cond, &_ThreadData.Mtx); + _iResult = ::mtx_unlock(&_ThreadData.Mtx); + Assert::AreEqual(_iResult, 0); + return 1; + }, &_ThreadData, 0, nullptr); + + Assert::IsNotNull(_hThread); + } + + Sleep(100); + + _iResult = cnd_signal(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + + uint32_t _uSignalCount = 0; + + for (auto& _hThread : _hThreads) + { + if (!_hThread) + continue; + if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0) + { + ++_uSignalCount; + CloseHandle(_hThread); + _hThread = nullptr; + } + } + + Assert::AreEqual(_uSignalCount, 1u); + _iResult = cnd_signal(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + + for (auto& _hThread : _hThreads) + { + if (!_hThread) + continue; + if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0) + { + ++_uSignalCount; + + CloseHandle(_hThread); + _hThread = nullptr; + } + } + Assert::AreEqual(_uSignalCount, 2u); + + _iResult = cnd_broadcast(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + + for (auto& _hThread : _hThreads) + { + if (!_hThread) + continue; + if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0) + { + ++_uSignalCount; + + CloseHandle(_hThread); + _hThread = nullptr; + } + } + + Assert::AreEqual(_uSignalCount, (uint32_t)std::size(_hThreads)); + } + + TEST_METHOD(_cnd_timedwait64) + { + struct ThreadData + { + mtx_t Mtx; + cnd_t Cond; + }; + + ThreadData _ThreadData; + + auto _iResult = ::mtx_init(&_ThreadData.Mtx, 0); + Assert::AreEqual(_iResult, 0); + + _iResult = ::cnd_init(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + + _iResult = ::mtx_lock(&_ThreadData.Mtx); + Assert::AreEqual(_iResult, 0); + + auto _hThread = (HANDLE)_beginthreadex( + 0, + 0, + [](void* _pUserData) -> unsigned + { + Sleep(5 * 1000); + auto& _ThreadData = *static_cast(_pUserData); + auto _iResult = cnd_signal(&_ThreadData.Cond); + Assert::AreEqual(_iResult, 0); + return 1; + }, &_ThreadData, 0, nullptr); + + Assert::IsNotNull(_hThread); + _timespec64 _Now; + + const auto _uStart0 = GetTickCount64(); + + _iResult = _timespec64_get(&_Now, TIME_UTC); + Assert::AreEqual(_iResult, TIME_UTC); + + _Now.tv_sec += 1; + _iResult = ::_cnd_timedwait64(&_ThreadData.Cond, &_ThreadData.Mtx, &_Now); + const auto _uTick1 = GetTickCount64() - _uStart0; + Assert::AreEqual(_iResult, (int)thrd_timedout); + Assert::IsTrue(_uTick1 >= 900 && _uTick1 <= 1200); + + _Now.tv_sec += 5; + _iResult = ::_cnd_timedwait64(&_ThreadData.Cond, &_ThreadData.Mtx, &_Now); + const auto _uTick2 = GetTickCount64() - _uStart0; + Assert::AreEqual(_iResult, (int)thrd_success); + Assert::IsTrue(_uTick2 >= 4800 && _uTick2 <= 5300); + + ::mtx_unlock(&_ThreadData.Mtx); + } + }; +} diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj index 573846b..47d7323 100644 --- a/UnitTest/UnitTest.vcxproj +++ b/UnitTest/UnitTest.vcxproj @@ -162,6 +162,7 @@ + Create diff --git a/UnitTest/UnitTest.vcxproj.filters b/UnitTest/UnitTest.vcxproj.filters index 1a2ad13..4d87292 100644 --- a/UnitTest/UnitTest.vcxproj.filters +++ b/UnitTest/UnitTest.vcxproj.filters @@ -39,6 +39,9 @@ 源文件\VC-LTL_HelperUnitTest\PowerShell + + 源文件 + diff --git a/UnitTest/pch.cpp b/UnitTest/pch.cpp index 0f09cf7..dac253a 100644 --- a/UnitTest/pch.cpp +++ b/UnitTest/pch.cpp @@ -73,15 +73,12 @@ LSTATUS RunCmd(LPCWSTR FilePath, CString CmdString, CString* pOutString, CString CloseHandle(pi.hThread); CloseHandle(pi.hProcess); - - //*OutString._Mylast() = NULL; if (pOutString) { - *pOutString = OutString; + const auto _iUtf8Length = OutString.GetLength(); + auto _iUtf16Length = MultiByteToWideChar(CP_UTF8, 0, OutString.GetString(), _iUtf8Length, pOutString->GetBuffer(_iUtf8Length), _iUtf8Length); + pOutString->ReleaseBufferSetLength(_iUtf16Length); } - - - //EXECDOSCMD. return lStatus; } diff --git a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj index 36888b9..73a1de2 100644 --- a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj +++ b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj @@ -696,6 +696,9 @@ true + + false + true diff --git a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters index a12ac2b..7a482c3 100644 --- a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters +++ b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters @@ -101,6 +101,9 @@ 源文件\ltl + + 源文件\ltl + diff --git a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj index f4917fa..889500f 100644 --- a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj +++ b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj @@ -835,6 +835,9 @@ true + + false + stdcpp17 true diff --git a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters index d9aa2a6..f33ab06 100644 --- a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters +++ b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters @@ -62,5 +62,8 @@ 源文件\ltl + + 源文件\ltl + \ No newline at end of file