// // Copyright 2021 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this // file was obtained (LICENSE.txt). A copy of the license may also be // found online at https://opensource.org/licenses/MIT. // // Windows threads. #include "core/nng_impl.h" #ifdef NNG_PLATFORM_WINDOWS typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE, PCWSTR); static HMODULE hKernel32; static pfnSetThreadDescription set_thread_desc; // mingw does not define InterlockedAddNoFence64, use the mingw equivalent #if defined(__MINGW32__) || defined(__MINGW64__) #define InterlockedAddNoFence(a, b) __atomic_add_fetch(a, b, __ATOMIC_RELAXED) #define InterlockedAddNoFence64(a, b) \ __atomic_add_fetch(a, b, __ATOMIC_RELAXED) #define InterlockedIncrementAcquire64(a) \ __atomic_add_fetch(a, 1, __ATOMIC_ACQUIRE) #define InterlockedDecrementRelease64(a) \ __atomic_fetch_sub(a, 1, __ATOMIC_RELEASE) #endif #include void * nni_alloc(size_t sz) { return (sz > 0 ? malloc(sz) : NULL); } void * nni_zalloc(size_t sz) { return (sz > 0 ? calloc(1, sz) : NULL); } void nni_free(void *b, size_t z) { NNI_ARG_UNUSED(z); free(b); } void nni_plat_mtx_init(nni_plat_mtx *mtx) { InitializeSRWLock(&mtx->srl); mtx->init = 1; } void nni_plat_mtx_fini(nni_plat_mtx *mtx) { mtx->init = 0; } void nni_plat_mtx_lock(nni_plat_mtx *mtx) { AcquireSRWLockExclusive(&mtx->srl); } void nni_plat_mtx_unlock(nni_plat_mtx *mtx) { ReleaseSRWLockExclusive(&mtx->srl); } void nni_rwlock_init(nni_rwlock *rwl) { InitializeSRWLock(&rwl->rwl); } void nni_rwlock_fini(nni_rwlock *rwl) { rwl->exclusive = FALSE; } void nni_rwlock_rdlock(nni_rwlock *rwl) { AcquireSRWLockShared(&rwl->rwl); } void nni_rwlock_wrlock(nni_rwlock *rwl) { AcquireSRWLockExclusive(&rwl->rwl); rwl->exclusive = TRUE; } void nni_rwlock_unlock(nni_rwlock *rwl) { if (rwl->exclusive) { rwl->exclusive = FALSE; ReleaseSRWLockExclusive(&rwl->rwl); } else { ReleaseSRWLockShared(&rwl->rwl); } } void nni_plat_cv_init(nni_plat_cv *cv, nni_plat_mtx *mtx) { InitializeConditionVariable(&cv->cv); cv->srl = &mtx->srl; } void nni_plat_cv_wake(nni_plat_cv *cv) { WakeAllConditionVariable(&cv->cv); } void nni_plat_cv_wake1(nni_plat_cv *cv) { WakeConditionVariable(&cv->cv); } void nni_plat_cv_wait(nni_plat_cv *cv) { (void) SleepConditionVariableSRW(&cv->cv, cv->srl, INFINITE, 0); } int nni_plat_cv_until(nni_plat_cv *cv, nni_time until) { nni_time now; DWORD msec; BOOL ok; now = nni_clock(); if (now > until) { msec = 0; } else { msec = (DWORD) (until - now); } ok = SleepConditionVariableSRW(&cv->cv, cv->srl, msec, 0); return (ok ? 0 : NNG_ETIMEDOUT); } void nni_plat_cv_fini(nni_plat_cv *cv) { NNI_ARG_UNUSED(cv); } bool nni_atomic_flag_test_and_set(nni_atomic_flag *f) { return (InterlockedExchange(&f->f, 1) != 0); } void nni_atomic_flag_reset(nni_atomic_flag *f) { InterlockedExchange(&f->f, 0); } void nni_atomic_set_bool(nni_atomic_bool *v, bool b) { InterlockedExchange(&v->v, (LONG) b); } bool nni_atomic_get_bool(nni_atomic_bool *v) { return ((bool) InterlockedAdd(&v->v, 0)); } bool nni_atomic_swap_bool(nni_atomic_bool *v, bool b) { return ((bool) InterlockedExchange(&v->v, (LONG) b)); } void nni_atomic_init_bool(nni_atomic_bool *v) { InterlockedExchange(&v->v, 0); } void nni_atomic_add64(nni_atomic_u64 *v, uint64_t bump) { InterlockedAddNoFence64(&v->v, (LONGLONG) bump); } void nni_atomic_sub64(nni_atomic_u64 *v, uint64_t bump) { // Windows lacks a sub, so we add the negative. InterlockedAddNoFence64(&v->v, (0ll - (LONGLONG) bump)); } uint64_t nni_atomic_get64(nni_atomic_u64 *v) { return ((uint64_t) (InterlockedExchangeAdd64(&v->v, 0))); } void nni_atomic_set64(nni_atomic_u64 *v, uint64_t u) { (void) InterlockedExchange64(&v->v, (LONGLONG) u); } uint64_t nni_atomic_swap64(nni_atomic_u64 *v, uint64_t u) { return ((uint64_t) (InterlockedExchange64(&v->v, (LONGLONG) u))); } void nni_atomic_init64(nni_atomic_u64 *v) { InterlockedExchange64(&v->v, 0); } void nni_atomic_inc64(nni_atomic_u64 *v) { #ifdef _WIN64 (void) InterlockedIncrementAcquire64(&v->v); #else (void) InterlockedIncrement64(&v->v); #endif } uint64_t nni_atomic_dec64_nv(nni_atomic_u64 *v) { #ifdef _WIN64 return ((uint64_t) (InterlockedDecrementRelease64(&v->v))); #else return ((uint64_t) (InterlockedDecrement64(&v->v))); #endif } bool nni_atomic_cas64(nni_atomic_u64 *v, uint64_t comp, uint64_t new) { uint64_t old; old = InterlockedCompareExchange64(&v->v, (LONG64) new, (LONG64) comp); return (old == comp); } void nni_atomic_add(nni_atomic_int *v, int bump) { InterlockedAddNoFence(&v->v, (LONG) bump); } void nni_atomic_sub(nni_atomic_int *v, int bump) { // Windows lacks a sub, so we add the negative. InterlockedAddNoFence(&v->v, (LONG) -bump); } int nni_atomic_get(nni_atomic_int *v) { return (InterlockedExchangeAdd(&v->v, 0)); } void nni_atomic_set(nni_atomic_int *v, int i) { (void) InterlockedExchange(&v->v, (LONG) i); } int nni_atomic_swap(nni_atomic_int *v, int i) { return (InterlockedExchange(&v->v, (LONG) i)); } void nni_atomic_init(nni_atomic_int *v) { InterlockedExchange(&v->v, 0); } void nni_atomic_inc(nni_atomic_int *v) { (void) InterlockedIncrementAcquire(&v->v); } int nni_atomic_dec_nv(nni_atomic_int *v) { return (InterlockedDecrementRelease(&v->v)); } bool nni_atomic_cas(nni_atomic_int *v, int comp, int new) { int old; old = InterlockedCompareExchange(&v->v, (LONG) new, (LONG) comp); return (old == comp); } static unsigned int __stdcall nni_plat_thr_main(void *arg) { nni_plat_thr *thr = arg; thr->id = GetCurrentThreadId(); thr->func(thr->arg); return (0); } int nni_plat_thr_init(nni_plat_thr *thr, void (*fn)(void *), void *arg) { thr->func = fn; thr->arg = arg; // We could probably even go down to 8k... but crypto for some // protocols might get bigger than this. 1MB is waaay too big. thr->handle = (HANDLE) _beginthreadex(NULL, 16384, nni_plat_thr_main, thr, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); if (thr->handle == NULL) { return (NNG_ENOMEM); // Best guess... } return (0); } void nni_plat_thr_fini(nni_plat_thr *thr) { if (WaitForSingleObject(thr->handle, INFINITE) == WAIT_FAILED) { nni_panic("waiting for thread failed!"); } if (CloseHandle(thr->handle) == 0) { nni_panic("close handle for thread failed!"); } } bool nni_plat_thr_is_self(nni_plat_thr *thr) { return (GetCurrentThreadId() == thr->id); } void nni_plat_thr_set_name(nni_plat_thr *thr, const char *name) { if (set_thread_desc != NULL) { wchar_t *wcs; size_t len; HANDLE h; if (thr == NULL) { h = GetCurrentThread(); } else { h = thr->handle; } len = strlen(name) + 1; if ((wcs = nni_alloc(len * 2)) == NULL) { return; } (void) MultiByteToWideChar(CP_UTF8, 0, name, len, wcs, len); set_thread_desc(h, wcs); nni_free(wcs, len * 2); } } static LONG plat_inited = 0; int nni_plat_ncpu(void) { SYSTEM_INFO info; int ncpu_max = nni_thr_get_ncpu_max(); int n = 0; GetSystemInfo(&info); n = ((int) (info.dwNumberOfProcessors)); if (n > ncpu_max) { return ncpu_max; } else { return n; } } int nni_plat_init(int (*helper)(void)) { int rv = 0; static SRWLOCK lock = SRWLOCK_INIT; if (plat_inited) { return (0); // fast path } AcquireSRWLockExclusive(&lock); if (!plat_inited) { // Let's look up the function to set thread descriptions. hKernel32 = LoadLibrary(TEXT("kernel32.dll")); if (hKernel32 != NULL) { set_thread_desc = (pfnSetThreadDescription) GetProcAddress( hKernel32, "SetThreadDescription"); } if (((rv = nni_win_io_sysinit()) != 0) || ((rv = nni_win_ipc_sysinit()) != 0) || ((rv = nni_win_tcp_sysinit()) != 0) || ((rv = nni_win_udp_sysinit()) != 0) || ((rv = nni_win_resolv_sysinit()) != 0)) { goto out; } helper(); plat_inited = 1; } out: ReleaseSRWLockExclusive(&lock); return (rv); } void nni_plat_fini(void) { nni_win_resolv_sysfini(); nni_win_ipc_sysfini(); nni_win_udp_sysfini(); nni_win_tcp_sysfini(); nni_win_io_sysfini(); WSACleanup(); if (hKernel32 != NULL) { FreeLibrary(hKernel32); } plat_inited = 0; } #endif