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