// Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include #include #include #include #include #include namespace { using _Thrd_start_t = int (*)(void*); struct _Thrd_binder { // bind function pointer and data to pass to thread entry point _Thrd_start_t func; void* data; _Cnd_t* cond; _Mtx_t* mtx; int* started; }; using _Thrd_callback_t = unsigned int(__stdcall*)(void*); unsigned int __stdcall _Thrd_runner(void* d) { // call thread function _Thrd_binder b = *static_cast<_Thrd_binder*>(d); _Mtx_lock(*b.mtx); *b.started = 1; _Cnd_signal(*b.cond); _Mtx_unlock(*b.mtx); const unsigned int res = b.func(b.data); _Cnd_do_broadcast_at_thread_exit(); return res; } } // unnamed namespace extern "C" { // TRANSITION, ABI: _Thrd_exit() is preserved for binary compatibility [[noreturn]] _CRTIMP2_PURE void __cdecl _Thrd_exit(int res) noexcept { // terminate execution of calling thread _endthreadex(res); } // TRANSITION, ABI: _Thrd_start() is preserved for binary compatibility _CRTIMP2_PURE _Thrd_result __cdecl _Thrd_start(_Thrd_t* thr, _Thrd_callback_t func, void* b) noexcept { // start a thread thr->_Hnd = reinterpret_cast(_beginthreadex(nullptr, 0, func, b, 0, &thr->_Id)); return thr->_Hnd == nullptr ? _Thrd_result::_Error : _Thrd_result::_Success; } _CRTIMP2_PURE _Thrd_result __cdecl _Thrd_join(_Thrd_t thr, int* code) noexcept { // returns when thread terminates if (WaitForSingleObjectEx(thr._Hnd, INFINITE, FALSE) == WAIT_FAILED) { return _Thrd_result::_Error; } if (code) { // TRANSITION, ABI: code is preserved for binary compatibility unsigned long res; if (!GetExitCodeThread(thr._Hnd, &res)) { return _Thrd_result::_Error; } *code = static_cast(res); } return CloseHandle(thr._Hnd) ? _Thrd_result::_Success : _Thrd_result::_Error; } _CRTIMP2_PURE _Thrd_result __cdecl _Thrd_detach(_Thrd_t thr) noexcept { // tell OS to release thread's resources when it terminates return CloseHandle(thr._Hnd) ? _Thrd_result::_Success : _Thrd_result::_Error; } // TRANSITION, ABI: _Thrd_sleep() is preserved for binary compatibility _CRTIMP2_PURE void __cdecl _Thrd_sleep(const _timespec64* xt) noexcept { // suspend thread until time xt _timespec64 now; _Timespec64_get_sys(&now); do { // sleep and check time Sleep(_Xtime_diff_to_millis2(xt, &now)); _Timespec64_get_sys(&now); } while (now.tv_sec < xt->tv_sec || now.tv_sec == xt->tv_sec && now.tv_nsec < xt->tv_nsec); } _CRTIMP2_PURE void __cdecl _Thrd_yield() noexcept { // surrender remainder of timeslice SwitchToThread(); } // TRANSITION, ABI: _Thrd_equal() is preserved for binary compatibility _CRTIMP2_PURE int __cdecl _Thrd_equal(_Thrd_t thr0, _Thrd_t thr1) noexcept { // 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 __cdecl _Thrd_current() noexcept { // return _Thrd_t identifying current thread _Thrd_t result; result._Hnd = nullptr; result._Id = GetCurrentThreadId(); return result; } _CRTIMP2_PURE _Thrd_id_t __cdecl _Thrd_id() noexcept { // return unique id for current thread return GetCurrentThreadId(); } _CRTIMP2_PURE unsigned int __cdecl _Thrd_hardware_concurrency() noexcept { // return number of processors // Most devices have only one processor group and thus have the same buffer_size. #ifdef _WIN64 constexpr int stack_buffer_size = 48; // 16 bytes per group #else // ^^^ 64-bit / 32-bit vvv constexpr int stack_buffer_size = 44; // 12 bytes per group #endif // ^^^ 32-bit ^^^ alignas(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) unsigned char stack_buffer[stack_buffer_size]; unsigned char* buffer_ptr = stack_buffer; DWORD buffer_size = stack_buffer_size; _STD unique_ptr new_buffer; // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex // The buffer "receives a sequence of variable-sized SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX structures". for (;;) { if (GetLogicalProcessorInformationEx(RelationProcessorPackage, reinterpret_cast(buffer_ptr), &buffer_size)) { unsigned int logical_processors = 0; while (buffer_size > 0) { // Each structure in the buffer describes a processor package (aka socket)... const auto structure_ptr = reinterpret_cast(buffer_ptr); const auto structure_size = structure_ptr->Size; // ... which contains one or more processor groups. for (WORD i = 0; i != structure_ptr->Processor.GroupCount; ++i) { logical_processors += _STD popcount(structure_ptr->Processor.GroupMask[i].Mask); } // Step forward to the next structure in the buffer. buffer_ptr += structure_size; buffer_size -= structure_size; } return logical_processors; } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return 0; // API failure } new_buffer.reset(::new (_STD nothrow) unsigned char[buffer_size]); if (!new_buffer) { return 0; // allocation failure } buffer_ptr = new_buffer.get(); } } // TRANSITION, ABI: _Thrd_create() is preserved for binary compatibility _CRTIMP2_PURE _Thrd_result __cdecl _Thrd_create(_Thrd_t* thr, _Thrd_start_t func, void* d) noexcept { // create thread _Thrd_result res; _Thrd_binder b; int started = 0; _Cnd_internal_imp_t cond_var{}; _Cnd_t cond = &cond_var; _Mtx_internal_imp_t mtx_var{}; _Mtx_t mtx = &mtx_var; _Mtx_init_in_situ(mtx, _Mtx_plain); b.func = func; b.data = d; b.cond = &cond; b.mtx = &mtx; b.started = &started; _Mtx_lock(mtx); if ((res = _Thrd_start(thr, _Thrd_runner, &b)) == _Thrd_result::_Success) { // wait for handshake while (!started) { _Cnd_wait(cond, mtx); } } _Mtx_unlock(mtx); return res; } } // 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. */