1 //
2 // Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
3 // Copyright 2018 Capitar IT Group BV <info@capitar.com>
4 //
5 // This software is supplied under the terms of the MIT License, a
6 // copy of which should be located in the distribution where this
7 // file was obtained (LICENSE.txt). A copy of the license may also be
8 // found online at https://opensource.org/licenses/MIT.
9 //
10
11 // Windows threads.
12
13 #include "core/nng_impl.h"
14
15 #ifdef NNG_PLATFORM_WINDOWS
16
17 typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE, PCWSTR);
18 static HMODULE hKernel32;
19
20 static pfnSetThreadDescription set_thread_desc;
21
22 // mingw does not define InterlockedAddNoFence64, use the mingw equivalent
23 #if defined(__MINGW32__) || defined(__MINGW64__)
24 #define InterlockedAddNoFence(a, b) __atomic_add_fetch(a, b, __ATOMIC_RELAXED)
25 #define InterlockedAddNoFence64(a, b) \
26 __atomic_add_fetch(a, b, __ATOMIC_RELAXED)
27 #define InterlockedIncrementAcquire64(a) \
28 __atomic_add_fetch(a, 1, __ATOMIC_ACQUIRE)
29 #define InterlockedDecrementRelease64(a) \
30 __atomic_fetch_sub(a, 1, __ATOMIC_RELEASE)
31 #endif
32
33 #include <stdlib.h>
34
35 void *
nni_alloc(size_t sz)36 nni_alloc(size_t sz)
37 {
38 return (sz > 0 ? malloc(sz) : NULL);
39 }
40
41 void *
nni_zalloc(size_t sz)42 nni_zalloc(size_t sz)
43 {
44 return (sz > 0 ? calloc(1, sz) : NULL);
45 }
46
47 void
nni_free(void * b,size_t z)48 nni_free(void *b, size_t z)
49 {
50 NNI_ARG_UNUSED(z);
51 free(b);
52 }
53
54 void
nni_plat_mtx_init(nni_plat_mtx * mtx)55 nni_plat_mtx_init(nni_plat_mtx *mtx)
56 {
57 InitializeSRWLock(&mtx->srl);
58 mtx->init = 1;
59 }
60
61 void
nni_plat_mtx_fini(nni_plat_mtx * mtx)62 nni_plat_mtx_fini(nni_plat_mtx *mtx)
63 {
64 mtx->init = 0;
65 }
66
67 void
nni_plat_mtx_lock(nni_plat_mtx * mtx)68 nni_plat_mtx_lock(nni_plat_mtx *mtx)
69 {
70 AcquireSRWLockExclusive(&mtx->srl);
71 }
72
73 void
nni_plat_mtx_unlock(nni_plat_mtx * mtx)74 nni_plat_mtx_unlock(nni_plat_mtx *mtx)
75 {
76 ReleaseSRWLockExclusive(&mtx->srl);
77 }
78
79 void
nni_rwlock_init(nni_rwlock * rwl)80 nni_rwlock_init(nni_rwlock *rwl)
81 {
82 InitializeSRWLock(&rwl->rwl);
83 }
84
85 void
nni_rwlock_fini(nni_rwlock * rwl)86 nni_rwlock_fini(nni_rwlock *rwl)
87 {
88 rwl->exclusive = FALSE;
89 }
90
91 void
nni_rwlock_rdlock(nni_rwlock * rwl)92 nni_rwlock_rdlock(nni_rwlock *rwl)
93 {
94 AcquireSRWLockShared(&rwl->rwl);
95 }
96
97 void
nni_rwlock_wrlock(nni_rwlock * rwl)98 nni_rwlock_wrlock(nni_rwlock *rwl)
99 {
100 AcquireSRWLockExclusive(&rwl->rwl);
101 rwl->exclusive = TRUE;
102 }
103
104 void
nni_rwlock_unlock(nni_rwlock * rwl)105 nni_rwlock_unlock(nni_rwlock *rwl)
106 {
107 if (rwl->exclusive) {
108 rwl->exclusive = FALSE;
109 ReleaseSRWLockExclusive(&rwl->rwl);
110 } else {
111 ReleaseSRWLockShared(&rwl->rwl);
112 }
113 }
114
115 void
nni_plat_cv_init(nni_plat_cv * cv,nni_plat_mtx * mtx)116 nni_plat_cv_init(nni_plat_cv *cv, nni_plat_mtx *mtx)
117 {
118 InitializeConditionVariable(&cv->cv);
119 cv->srl = &mtx->srl;
120 }
121
122 void
nni_plat_cv_wake(nni_plat_cv * cv)123 nni_plat_cv_wake(nni_plat_cv *cv)
124 {
125 WakeAllConditionVariable(&cv->cv);
126 }
127
128 void
nni_plat_cv_wake1(nni_plat_cv * cv)129 nni_plat_cv_wake1(nni_plat_cv *cv)
130 {
131 WakeConditionVariable(&cv->cv);
132 }
133
134 void
nni_plat_cv_wait(nni_plat_cv * cv)135 nni_plat_cv_wait(nni_plat_cv *cv)
136 {
137 (void) SleepConditionVariableSRW(&cv->cv, cv->srl, INFINITE, 0);
138 }
139
140 int
nni_plat_cv_until(nni_plat_cv * cv,nni_time until)141 nni_plat_cv_until(nni_plat_cv *cv, nni_time until)
142 {
143 nni_time now;
144 DWORD msec;
145 BOOL ok;
146
147 now = nni_clock();
148 if (now > until) {
149 msec = 0;
150 } else {
151 msec = (DWORD) (until - now);
152 }
153
154 ok = SleepConditionVariableSRW(&cv->cv, cv->srl, msec, 0);
155 return (ok ? 0 : NNG_ETIMEDOUT);
156 }
157
158 void
nni_plat_cv_fini(nni_plat_cv * cv)159 nni_plat_cv_fini(nni_plat_cv *cv)
160 {
161 NNI_ARG_UNUSED(cv);
162 }
163
164 bool
nni_atomic_flag_test_and_set(nni_atomic_flag * f)165 nni_atomic_flag_test_and_set(nni_atomic_flag *f)
166 {
167 return (InterlockedExchange(&f->f, 1) != 0);
168 }
169
170 void
nni_atomic_flag_reset(nni_atomic_flag * f)171 nni_atomic_flag_reset(nni_atomic_flag *f)
172 {
173 InterlockedExchange(&f->f, 0);
174 }
175
176 void
nni_atomic_set_bool(nni_atomic_bool * v,bool b)177 nni_atomic_set_bool(nni_atomic_bool *v, bool b)
178 {
179 InterlockedExchange(&v->v, (LONG) b);
180 }
181
182 bool
nni_atomic_get_bool(nni_atomic_bool * v)183 nni_atomic_get_bool(nni_atomic_bool *v)
184 {
185 return ((bool) InterlockedAdd(&v->v, 0));
186 }
187
188 bool
nni_atomic_swap_bool(nni_atomic_bool * v,bool b)189 nni_atomic_swap_bool(nni_atomic_bool *v, bool b)
190 {
191 return ((bool) InterlockedExchange(&v->v, (LONG) b));
192 }
193
194 void
nni_atomic_init_bool(nni_atomic_bool * v)195 nni_atomic_init_bool(nni_atomic_bool *v)
196 {
197 InterlockedExchange(&v->v, 0);
198 }
199
200 void
nni_atomic_add64(nni_atomic_u64 * v,uint64_t bump)201 nni_atomic_add64(nni_atomic_u64 *v, uint64_t bump)
202 {
203 InterlockedAddNoFence64(&v->v, (LONGLONG) bump);
204 }
205
206 void
nni_atomic_sub64(nni_atomic_u64 * v,uint64_t bump)207 nni_atomic_sub64(nni_atomic_u64 *v, uint64_t bump)
208 {
209 // Windows lacks a sub, so we add the negative.
210 InterlockedAddNoFence64(&v->v, (0ll - (LONGLONG) bump));
211 }
212
213 uint64_t
nni_atomic_get64(nni_atomic_u64 * v)214 nni_atomic_get64(nni_atomic_u64 *v)
215 {
216
217 return ((uint64_t) (InterlockedExchangeAdd64(&v->v, 0)));
218 }
219
220 void
nni_atomic_set64(nni_atomic_u64 * v,uint64_t u)221 nni_atomic_set64(nni_atomic_u64 *v, uint64_t u)
222 {
223 (void) InterlockedExchange64(&v->v, (LONGLONG) u);
224 }
225
226 uint64_t
nni_atomic_swap64(nni_atomic_u64 * v,uint64_t u)227 nni_atomic_swap64(nni_atomic_u64 *v, uint64_t u)
228 {
229 return ((uint64_t) (InterlockedExchange64(&v->v, (LONGLONG) u)));
230 }
231
232 void
nni_atomic_init64(nni_atomic_u64 * v)233 nni_atomic_init64(nni_atomic_u64 *v)
234 {
235 InterlockedExchange64(&v->v, 0);
236 }
237
238 void
nni_atomic_inc64(nni_atomic_u64 * v)239 nni_atomic_inc64(nni_atomic_u64 *v)
240 {
241 #ifdef _WIN64
242 (void) InterlockedIncrementAcquire64(&v->v);
243 #else
244 (void) InterlockedIncrement64(&v->v);
245 #endif
246 }
247
248 uint64_t
nni_atomic_dec64_nv(nni_atomic_u64 * v)249 nni_atomic_dec64_nv(nni_atomic_u64 *v)
250 {
251 #ifdef _WIN64
252 return ((uint64_t) (InterlockedDecrementRelease64(&v->v)));
253 #else
254 return ((uint64_t) (InterlockedDecrement64(&v->v)));
255 #endif
256 }
257
258 bool
nni_atomic_cas64(nni_atomic_u64 * v,uint64_t comp,uint64_t new)259 nni_atomic_cas64(nni_atomic_u64 *v, uint64_t comp, uint64_t new)
260 {
261 uint64_t old;
262 old = InterlockedCompareExchange64(&v->v, (LONG64) new, (LONG64) comp);
263 return (old == comp);
264 }
265
266 void
nni_atomic_add(nni_atomic_int * v,int bump)267 nni_atomic_add(nni_atomic_int *v, int bump)
268 {
269 InterlockedAddNoFence(&v->v, (LONG) bump);
270 }
271
272 void
nni_atomic_sub(nni_atomic_int * v,int bump)273 nni_atomic_sub(nni_atomic_int *v, int bump)
274 {
275 // Windows lacks a sub, so we add the negative.
276 InterlockedAddNoFence(&v->v, (LONG) -bump);
277 }
278
279 int
nni_atomic_get(nni_atomic_int * v)280 nni_atomic_get(nni_atomic_int *v)
281 {
282
283 return (InterlockedExchangeAdd(&v->v, 0));
284 }
285
286 void
nni_atomic_set(nni_atomic_int * v,int i)287 nni_atomic_set(nni_atomic_int *v, int i)
288 {
289 (void) InterlockedExchange(&v->v, (LONG) i);
290 }
291
292 int
nni_atomic_swap(nni_atomic_int * v,int i)293 nni_atomic_swap(nni_atomic_int *v, int i)
294 {
295 return (InterlockedExchange(&v->v, (LONG) i));
296 }
297
298 void
nni_atomic_init(nni_atomic_int * v)299 nni_atomic_init(nni_atomic_int *v)
300 {
301 InterlockedExchange(&v->v, 0);
302 }
303
304 void
nni_atomic_inc(nni_atomic_int * v)305 nni_atomic_inc(nni_atomic_int *v)
306 {
307 (void) InterlockedIncrementAcquire(&v->v);
308 }
309
310 int
nni_atomic_dec_nv(nni_atomic_int * v)311 nni_atomic_dec_nv(nni_atomic_int *v)
312 {
313 return (InterlockedDecrementRelease(&v->v));
314 }
315
316 bool
nni_atomic_cas(nni_atomic_int * v,int comp,int new)317 nni_atomic_cas(nni_atomic_int *v, int comp, int new)
318 {
319 int old;
320 old = InterlockedCompareExchange(&v->v, (LONG) new, (LONG) comp);
321 return (old == comp);
322 }
323
nni_plat_thr_main(void * arg)324 static unsigned int __stdcall nni_plat_thr_main(void *arg)
325 {
326 nni_plat_thr *thr = arg;
327
328 thr->id = GetCurrentThreadId();
329 thr->func(thr->arg);
330 return (0);
331 }
332
333 int
nni_plat_thr_init(nni_plat_thr * thr,void (* fn)(void *),void * arg)334 nni_plat_thr_init(nni_plat_thr *thr, void (*fn)(void *), void *arg)
335 {
336 thr->func = fn;
337 thr->arg = arg;
338
339 // We could probably even go down to 8k... but crypto for some
340 // protocols might get bigger than this. 1MB is waaay too big.
341 thr->handle = (HANDLE) _beginthreadex(NULL, 16384, nni_plat_thr_main,
342 thr, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
343 if (thr->handle == NULL) {
344 return (NNG_ENOMEM); // Best guess...
345 }
346 return (0);
347 }
348
349 void
nni_plat_thr_fini(nni_plat_thr * thr)350 nni_plat_thr_fini(nni_plat_thr *thr)
351 {
352 if (WaitForSingleObject(thr->handle, INFINITE) == WAIT_FAILED) {
353 nni_panic("waiting for thread failed!");
354 }
355 if (CloseHandle(thr->handle) == 0) {
356 nni_panic("close handle for thread failed!");
357 }
358 }
359
360 bool
nni_plat_thr_is_self(nni_plat_thr * thr)361 nni_plat_thr_is_self(nni_plat_thr *thr)
362 {
363 return (GetCurrentThreadId() == thr->id);
364 }
365
366 void
nni_plat_thr_set_name(nni_plat_thr * thr,const char * name)367 nni_plat_thr_set_name(nni_plat_thr *thr, const char *name)
368 {
369 if (set_thread_desc != NULL) {
370 wchar_t *wcs;
371 size_t len;
372 HANDLE h;
373
374 if (thr == NULL) {
375 h = GetCurrentThread();
376 } else {
377 h = thr->handle;
378 }
379
380 len = strlen(name) + 1;
381 if ((wcs = nni_alloc(len * 2)) == NULL) {
382 return;
383 }
384 (void) MultiByteToWideChar(CP_UTF8, 0, name, len, wcs, len);
385 set_thread_desc(h, wcs);
386 nni_free(wcs, len * 2);
387 }
388 }
389
390 static LONG plat_inited = 0;
391
392 int
nni_plat_ncpu(void)393 nni_plat_ncpu(void)
394 {
395 SYSTEM_INFO info;
396
397 GetSystemInfo(&info);
398 return ((int) (info.dwNumberOfProcessors));
399 }
400
401 int
nni_plat_init(int (* helper)(void))402 nni_plat_init(int (*helper)(void))
403 {
404 int rv = 0;
405 static SRWLOCK lock = SRWLOCK_INIT;
406
407 if (plat_inited) {
408 return (0); // fast path
409 }
410
411 AcquireSRWLockExclusive(&lock);
412
413 if (!plat_inited) {
414 // Let's look up the function to set thread descriptions.
415 hKernel32 = LoadLibrary(TEXT("kernel32.dll"));
416 if (hKernel32 != NULL) {
417 set_thread_desc =
418 (pfnSetThreadDescription) GetProcAddress(
419 hKernel32, "SetThreadDescription");
420 }
421
422 if (((rv = nni_win_io_sysinit()) != 0) ||
423 ((rv = nni_win_ipc_sysinit()) != 0) ||
424 ((rv = nni_win_tcp_sysinit()) != 0) ||
425 ((rv = nni_win_udp_sysinit()) != 0) ||
426 ((rv = nni_win_resolv_sysinit()) != 0)) {
427 goto out;
428 }
429
430 helper();
431 plat_inited = 1;
432 }
433
434 out:
435 ReleaseSRWLockExclusive(&lock);
436
437 return (rv);
438 }
439
440 void
nni_plat_fini(void)441 nni_plat_fini(void)
442 {
443 nni_win_resolv_sysfini();
444 nni_win_ipc_sysfini();
445 nni_win_udp_sysfini();
446 nni_win_tcp_sysfini();
447 nni_win_io_sysfini();
448 WSACleanup();
449 if (hKernel32 != NULL) {
450 FreeLibrary(hKernel32);
451 }
452 plat_inited = 0;
453 }
454
455 #endif
456