xref: /libuv/src/win/thread.c (revision 264bb335)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include <assert.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 
26 #if defined(__MINGW64_VERSION_MAJOR)
27 /* MemoryBarrier expands to __mm_mfence in some cases (x86+sse2), which may
28  * require this header in some versions of mingw64. */
29 #include <intrin.h>
30 #endif
31 
32 #include "uv.h"
33 #include "internal.h"
34 
35 typedef void (*uv__once_cb)(void);
36 
37 typedef struct {
38   uv__once_cb callback;
39 } uv__once_data_t;
40 
uv__once_inner(INIT_ONCE * once,void * param,void ** context)41 static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) {
42   uv__once_data_t* data = param;
43 
44   data->callback();
45 
46   return TRUE;
47 }
48 
uv_once(uv_once_t * guard,uv__once_cb callback)49 void uv_once(uv_once_t* guard, uv__once_cb callback) {
50   uv__once_data_t data = { .callback = callback };
51   InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL);
52 }
53 
54 
55 /* Verify that uv_thread_t can be stored in a TLS slot. */
56 STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
57 
58 static uv_key_t uv__current_thread_key;
59 static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
60 
61 
uv__init_current_thread_key(void)62 static void uv__init_current_thread_key(void) {
63   if (uv_key_create(&uv__current_thread_key))
64     abort();
65 }
66 
67 
68 struct thread_ctx {
69   void (*entry)(void* arg);
70   void* arg;
71   uv_thread_t self;
72 };
73 
74 
uv__thread_start(void * arg)75 static UINT __stdcall uv__thread_start(void* arg) {
76   struct thread_ctx *ctx_p;
77   struct thread_ctx ctx;
78 
79   ctx_p = arg;
80   ctx = *ctx_p;
81   uv__free(ctx_p);
82 
83   uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
84   uv_key_set(&uv__current_thread_key, ctx.self);
85 
86   ctx.entry(ctx.arg);
87 
88   return 0;
89 }
90 
91 
uv_thread_create(uv_thread_t * tid,void (* entry)(void * arg),void * arg)92 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
93   uv_thread_options_t params;
94   params.flags = UV_THREAD_NO_FLAGS;
95   return uv_thread_create_ex(tid, &params, entry, arg);
96 }
97 
98 
uv_thread_detach(uv_thread_t * tid)99 int uv_thread_detach(uv_thread_t *tid) {
100   if (CloseHandle(*tid) == 0)
101     return uv_translate_sys_error(GetLastError());
102 
103   return 0;
104 }
105 
106 
uv_thread_create_ex(uv_thread_t * tid,const uv_thread_options_t * params,void (* entry)(void * arg),void * arg)107 int uv_thread_create_ex(uv_thread_t* tid,
108                         const uv_thread_options_t* params,
109                         void (*entry)(void *arg),
110                         void *arg) {
111   struct thread_ctx* ctx;
112   int err;
113   HANDLE thread;
114   SYSTEM_INFO sysinfo;
115   size_t stack_size;
116   size_t pagesize;
117 
118   stack_size =
119       params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
120 
121   if (stack_size != 0) {
122     GetNativeSystemInfo(&sysinfo);
123     pagesize = (size_t)sysinfo.dwPageSize;
124     /* Round up to the nearest page boundary. */
125     stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
126 
127     if ((unsigned)stack_size != stack_size)
128       return UV_EINVAL;
129   }
130 
131   ctx = uv__malloc(sizeof(*ctx));
132   if (ctx == NULL)
133     return UV_ENOMEM;
134 
135   ctx->entry = entry;
136   ctx->arg = arg;
137 
138   /* Create the thread in suspended state so we have a chance to pass
139    * its own creation handle to it */
140   thread = (HANDLE) _beginthreadex(NULL,
141                                    (unsigned)stack_size,
142                                    uv__thread_start,
143                                    ctx,
144                                    CREATE_SUSPENDED,
145                                    NULL);
146   if (thread == NULL) {
147     err = errno;
148     uv__free(ctx);
149   } else {
150     err = 0;
151     *tid = thread;
152     ctx->self = thread;
153     ResumeThread(thread);
154   }
155 
156   switch (err) {
157     case 0:
158       return 0;
159     case EACCES:
160       return UV_EACCES;
161     case EAGAIN:
162       return UV_EAGAIN;
163     case EINVAL:
164       return UV_EINVAL;
165   }
166 
167   return UV_EIO;
168 }
169 
uv_thread_setaffinity(uv_thread_t * tid,char * cpumask,char * oldmask,size_t mask_size)170 int uv_thread_setaffinity(uv_thread_t* tid,
171                           char* cpumask,
172                           char* oldmask,
173                           size_t mask_size) {
174   int i;
175   HANDLE hproc;
176   DWORD_PTR procmask;
177   DWORD_PTR sysmask;
178   DWORD_PTR threadmask;
179   DWORD_PTR oldthreadmask;
180   int cpumasksize;
181 
182   cpumasksize = uv_cpumask_size();
183   assert(cpumasksize > 0);
184   if (mask_size < (size_t)cpumasksize)
185     return UV_EINVAL;
186 
187   hproc = GetCurrentProcess();
188   if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
189     return uv_translate_sys_error(GetLastError());
190 
191   threadmask = 0;
192   for (i = 0; i < cpumasksize; i++) {
193     if (cpumask[i]) {
194       if (procmask & (1 << i))
195         threadmask |= 1 << i;
196       else
197         return UV_EINVAL;
198     }
199   }
200 
201   oldthreadmask = SetThreadAffinityMask(*tid, threadmask);
202   if (oldthreadmask == 0)
203     return uv_translate_sys_error(GetLastError());
204 
205   if (oldmask != NULL) {
206     for (i = 0; i < cpumasksize; i++)
207       oldmask[i] = (oldthreadmask >> i) & 1;
208   }
209 
210   return 0;
211 }
212 
uv_thread_getaffinity(uv_thread_t * tid,char * cpumask,size_t mask_size)213 int uv_thread_getaffinity(uv_thread_t* tid,
214                           char* cpumask,
215                           size_t mask_size) {
216   int i;
217   HANDLE hproc;
218   DWORD_PTR procmask;
219   DWORD_PTR sysmask;
220   DWORD_PTR threadmask;
221   int cpumasksize;
222 
223   cpumasksize = uv_cpumask_size();
224   assert(cpumasksize > 0);
225   if (mask_size < (size_t)cpumasksize)
226     return UV_EINVAL;
227 
228   hproc = GetCurrentProcess();
229   if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
230     return uv_translate_sys_error(GetLastError());
231 
232   threadmask = SetThreadAffinityMask(*tid, procmask);
233   if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0)
234     return uv_translate_sys_error(GetLastError());
235 
236   for (i = 0; i < cpumasksize; i++)
237     cpumask[i] = (threadmask >> i) & 1;
238 
239   return 0;
240 }
241 
uv_thread_getcpu(void)242 int uv_thread_getcpu(void) {
243   return GetCurrentProcessorNumber();
244 }
245 
uv_thread_self(void)246 uv_thread_t uv_thread_self(void) {
247   uv_thread_t key;
248   uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
249   key = uv_key_get(&uv__current_thread_key);
250   if (key == NULL) {
251       /* If the thread wasn't started by uv_thread_create (such as the main
252        * thread), we assign an id to it now. */
253       if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
254                            GetCurrentProcess(), &key, 0,
255                            FALSE, DUPLICATE_SAME_ACCESS)) {
256           uv_fatal_error(GetLastError(), "DuplicateHandle");
257       }
258       uv_key_set(&uv__current_thread_key, key);
259   }
260   return key;
261 }
262 
263 
uv_thread_join(uv_thread_t * tid)264 int uv_thread_join(uv_thread_t *tid) {
265   if (WaitForSingleObject(*tid, INFINITE))
266     return uv_translate_sys_error(GetLastError());
267   else {
268     CloseHandle(*tid);
269     *tid = 0;
270     MemoryBarrier();  /* For feature parity with pthread_join(). */
271     return 0;
272   }
273 }
274 
275 
uv_thread_equal(const uv_thread_t * t1,const uv_thread_t * t2)276 int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
277   return *t1 == *t2;
278 }
279 
280 
uv_thread_setname(const char * name)281 int uv_thread_setname(const char* name) {
282   HRESULT hr;
283   WCHAR* namew;
284   int err;
285   char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
286 
287   if (name == NULL)
288     return UV_EINVAL;
289 
290   strncpy(namebuf, name, sizeof(namebuf) - 1);
291   namebuf[sizeof(namebuf) - 1] = '\0';
292 
293   namew = NULL;
294   err = uv__convert_utf8_to_utf16(namebuf, &namew);
295   if (err)
296     return err;
297 
298   hr = SetThreadDescription(GetCurrentThread(), namew);
299   uv__free(namew);
300   if (FAILED(hr))
301     return uv_translate_sys_error(HRESULT_CODE(hr));
302 
303   return 0;
304 }
305 
306 
uv_thread_getname(uv_thread_t * tid,char * name,size_t size)307 int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
308   HRESULT hr;
309   WCHAR* namew;
310   char* thread_name;
311   size_t buf_size;
312   int r;
313   DWORD exit_code;
314 
315   if (name == NULL || size == 0)
316     return UV_EINVAL;
317 
318   if (tid == NULL || *tid == NULL)
319     return UV_EINVAL;
320 
321   /* Check if the thread handle is valid */
322   if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE)
323     return UV_ENOENT;
324 
325   namew = NULL;
326   thread_name = NULL;
327   hr = GetThreadDescription(*tid, &namew);
328   if (FAILED(hr))
329     return uv_translate_sys_error(HRESULT_CODE(hr));
330 
331   buf_size = size;
332   r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size);
333   if (r == UV_ENOBUFS) {
334     r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name);
335     if (r == 0) {
336       uv__strscpy(name, thread_name, size);
337       uv__free(thread_name);
338     }
339   }
340 
341   LocalFree(namew);
342   return r;
343 }
344 
345 
uv_mutex_init(uv_mutex_t * mutex)346 int uv_mutex_init(uv_mutex_t* mutex) {
347   InitializeCriticalSection(mutex);
348   return 0;
349 }
350 
351 
uv_mutex_init_recursive(uv_mutex_t * mutex)352 int uv_mutex_init_recursive(uv_mutex_t* mutex) {
353   return uv_mutex_init(mutex);
354 }
355 
356 
uv_mutex_destroy(uv_mutex_t * mutex)357 void uv_mutex_destroy(uv_mutex_t* mutex) {
358   DeleteCriticalSection(mutex);
359 }
360 
361 
uv_mutex_lock(uv_mutex_t * mutex)362 void uv_mutex_lock(uv_mutex_t* mutex) {
363   EnterCriticalSection(mutex);
364 }
365 
366 
uv_mutex_trylock(uv_mutex_t * mutex)367 int uv_mutex_trylock(uv_mutex_t* mutex) {
368   if (TryEnterCriticalSection(mutex))
369     return 0;
370   else
371     return UV_EBUSY;
372 }
373 
374 
uv_mutex_unlock(uv_mutex_t * mutex)375 void uv_mutex_unlock(uv_mutex_t* mutex) {
376   LeaveCriticalSection(mutex);
377 }
378 
379 /* Ensure that the ABI for this type remains stable in v1.x */
380 #ifdef _WIN64
381 STATIC_ASSERT(sizeof(uv_rwlock_t) == 80);
382 #else
383 STATIC_ASSERT(sizeof(uv_rwlock_t) == 48);
384 #endif
385 
uv_rwlock_init(uv_rwlock_t * rwlock)386 int uv_rwlock_init(uv_rwlock_t* rwlock) {
387   memset(rwlock, 0, sizeof(*rwlock));
388   InitializeSRWLock(&rwlock->read_write_lock_);
389 
390   return 0;
391 }
392 
393 
uv_rwlock_destroy(uv_rwlock_t * rwlock)394 void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
395   /* SRWLock does not need explicit destruction so long as there are no waiting threads
396      See: https://docs.microsoft.com/windows/win32/api/synchapi/nf-synchapi-initializesrwlock#remarks */
397 }
398 
399 
uv_rwlock_rdlock(uv_rwlock_t * rwlock)400 void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
401   AcquireSRWLockShared(&rwlock->read_write_lock_);
402 }
403 
404 
uv_rwlock_tryrdlock(uv_rwlock_t * rwlock)405 int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
406   if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_))
407     return UV_EBUSY;
408 
409   return 0;
410 }
411 
412 
uv_rwlock_rdunlock(uv_rwlock_t * rwlock)413 void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
414   ReleaseSRWLockShared(&rwlock->read_write_lock_);
415 }
416 
417 
uv_rwlock_wrlock(uv_rwlock_t * rwlock)418 void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
419   AcquireSRWLockExclusive(&rwlock->read_write_lock_);
420 }
421 
422 
uv_rwlock_trywrlock(uv_rwlock_t * rwlock)423 int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
424   if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_))
425     return UV_EBUSY;
426 
427   return 0;
428 }
429 
430 
uv_rwlock_wrunlock(uv_rwlock_t * rwlock)431 void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
432   ReleaseSRWLockExclusive(&rwlock->read_write_lock_);
433 }
434 
435 
uv_sem_init(uv_sem_t * sem,unsigned int value)436 int uv_sem_init(uv_sem_t* sem, unsigned int value) {
437   *sem = CreateSemaphore(NULL, value, INT_MAX, NULL);
438   if (*sem == NULL)
439     return uv_translate_sys_error(GetLastError());
440   else
441     return 0;
442 }
443 
444 
uv_sem_destroy(uv_sem_t * sem)445 void uv_sem_destroy(uv_sem_t* sem) {
446   if (!CloseHandle(*sem))
447     abort();
448 }
449 
450 
uv_sem_post(uv_sem_t * sem)451 void uv_sem_post(uv_sem_t* sem) {
452   if (!ReleaseSemaphore(*sem, 1, NULL))
453     abort();
454 }
455 
456 
uv_sem_wait(uv_sem_t * sem)457 void uv_sem_wait(uv_sem_t* sem) {
458   if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0)
459     abort();
460 }
461 
462 
uv_sem_trywait(uv_sem_t * sem)463 int uv_sem_trywait(uv_sem_t* sem) {
464   DWORD r = WaitForSingleObject(*sem, 0);
465 
466   if (r == WAIT_OBJECT_0)
467     return 0;
468 
469   if (r == WAIT_TIMEOUT)
470     return UV_EAGAIN;
471 
472   abort();
473   return -1; /* Satisfy the compiler. */
474 }
475 
476 
uv_cond_init(uv_cond_t * cond)477 int uv_cond_init(uv_cond_t* cond) {
478   InitializeConditionVariable(&cond->cond_var);
479   return 0;
480 }
481 
482 
uv_cond_destroy(uv_cond_t * cond)483 void uv_cond_destroy(uv_cond_t* cond) {
484   /* nothing to do */
485   (void) &cond;
486 }
487 
488 
uv_cond_signal(uv_cond_t * cond)489 void uv_cond_signal(uv_cond_t* cond) {
490   WakeConditionVariable(&cond->cond_var);
491 }
492 
493 
uv_cond_broadcast(uv_cond_t * cond)494 void uv_cond_broadcast(uv_cond_t* cond) {
495   WakeAllConditionVariable(&cond->cond_var);
496 }
497 
498 
uv_cond_wait(uv_cond_t * cond,uv_mutex_t * mutex)499 void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
500   if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
501     abort();
502 }
503 
504 
uv_cond_timedwait(uv_cond_t * cond,uv_mutex_t * mutex,uint64_t timeout)505 int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
506   if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
507     return 0;
508   if (GetLastError() != ERROR_TIMEOUT)
509     abort();
510   return UV_ETIMEDOUT;
511 }
512 
513 
uv_key_create(uv_key_t * key)514 int uv_key_create(uv_key_t* key) {
515   key->tls_index = TlsAlloc();
516   if (key->tls_index == TLS_OUT_OF_INDEXES)
517     return UV_ENOMEM;
518   return 0;
519 }
520 
521 
uv_key_delete(uv_key_t * key)522 void uv_key_delete(uv_key_t* key) {
523   if (TlsFree(key->tls_index) == FALSE)
524     abort();
525   key->tls_index = TLS_OUT_OF_INDEXES;
526 }
527 
528 
uv_key_get(uv_key_t * key)529 void* uv_key_get(uv_key_t* key) {
530   void* value;
531 
532   value = TlsGetValue(key->tls_index);
533   if (value == NULL)
534     if (GetLastError() != ERROR_SUCCESS)
535       abort();
536 
537   return value;
538 }
539 
540 
uv_key_set(uv_key_t * key,void * value)541 void uv_key_set(uv_key_t* key, void* value) {
542   if (TlsSetValue(key->tls_index, value) == FALSE)
543     abort();
544 }
545