xref: /third_party/libuv/src/win/thread.c (revision e66f31c5)
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
35static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
36  DWORD result;
37  HANDLE existing_event, created_event;
38
39  created_event = CreateEvent(NULL, 1, 0, NULL);
40  if (created_event == 0) {
41    /* Could fail in a low-memory situation? */
42    uv_fatal_error(GetLastError(), "CreateEvent");
43  }
44
45  existing_event = InterlockedCompareExchangePointer(&guard->event,
46                                                     created_event,
47                                                     NULL);
48
49  if (existing_event == NULL) {
50    /* We won the race */
51    callback();
52
53    result = SetEvent(created_event);
54    assert(result);
55    guard->ran = 1;
56
57  } else {
58    /* We lost the race. Destroy the event we created and wait for the existing
59     * one to become signaled. */
60    CloseHandle(created_event);
61    result = WaitForSingleObject(existing_event, INFINITE);
62    assert(result == WAIT_OBJECT_0);
63  }
64}
65
66
67void uv_once(uv_once_t* guard, void (*callback)(void)) {
68  /* Fast case - avoid WaitForSingleObject. */
69  if (guard->ran) {
70    return;
71  }
72
73  uv__once_inner(guard, callback);
74}
75
76
77/* Verify that uv_thread_t can be stored in a TLS slot. */
78STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
79
80static uv_key_t uv__current_thread_key;
81static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
82
83
84static void uv__init_current_thread_key(void) {
85  if (uv_key_create(&uv__current_thread_key))
86    abort();
87}
88
89
90struct thread_ctx {
91  void (*entry)(void* arg);
92  void* arg;
93  uv_thread_t self;
94};
95
96
97static UINT __stdcall uv__thread_start(void* arg) {
98  struct thread_ctx *ctx_p;
99  struct thread_ctx ctx;
100
101  ctx_p = arg;
102  ctx = *ctx_p;
103  uv__free(ctx_p);
104
105  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
106  uv_key_set(&uv__current_thread_key, ctx.self);
107
108  ctx.entry(ctx.arg);
109
110  return 0;
111}
112
113
114int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
115  uv_thread_options_t params;
116  params.flags = UV_THREAD_NO_FLAGS;
117  return uv_thread_create_ex(tid, &params, entry, arg);
118}
119
120int uv_thread_create_ex(uv_thread_t* tid,
121                        const uv_thread_options_t* params,
122                        void (*entry)(void *arg),
123                        void *arg) {
124  struct thread_ctx* ctx;
125  int err;
126  HANDLE thread;
127  SYSTEM_INFO sysinfo;
128  size_t stack_size;
129  size_t pagesize;
130
131  stack_size =
132      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
133
134  if (stack_size != 0) {
135    GetNativeSystemInfo(&sysinfo);
136    pagesize = (size_t)sysinfo.dwPageSize;
137    /* Round up to the nearest page boundary. */
138    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
139
140    if ((unsigned)stack_size != stack_size)
141      return UV_EINVAL;
142  }
143
144  ctx = uv__malloc(sizeof(*ctx));
145  if (ctx == NULL)
146    return UV_ENOMEM;
147
148  ctx->entry = entry;
149  ctx->arg = arg;
150
151  /* Create the thread in suspended state so we have a chance to pass
152   * its own creation handle to it */
153  thread = (HANDLE) _beginthreadex(NULL,
154                                   (unsigned)stack_size,
155                                   uv__thread_start,
156                                   ctx,
157                                   CREATE_SUSPENDED,
158                                   NULL);
159  if (thread == NULL) {
160    err = errno;
161    uv__free(ctx);
162  } else {
163    err = 0;
164    *tid = thread;
165    ctx->self = thread;
166    ResumeThread(thread);
167  }
168
169  switch (err) {
170    case 0:
171      return 0;
172    case EACCES:
173      return UV_EACCES;
174    case EAGAIN:
175      return UV_EAGAIN;
176    case EINVAL:
177      return UV_EINVAL;
178  }
179
180  return UV_EIO;
181}
182
183int uv_thread_setaffinity(uv_thread_t* tid,
184                          char* cpumask,
185                          char* oldmask,
186                          size_t mask_size) {
187  int i;
188  HANDLE hproc;
189  DWORD_PTR procmask;
190  DWORD_PTR sysmask;
191  DWORD_PTR threadmask;
192  DWORD_PTR oldthreadmask;
193  int cpumasksize;
194
195  cpumasksize = uv_cpumask_size();
196  assert(cpumasksize > 0);
197  if (mask_size < (size_t)cpumasksize)
198    return UV_EINVAL;
199
200  hproc = GetCurrentProcess();
201  if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
202    return uv_translate_sys_error(GetLastError());
203
204  threadmask = 0;
205  for (i = 0; i < cpumasksize; i++) {
206    if (cpumask[i]) {
207      if (procmask & (1 << i))
208        threadmask |= 1 << i;
209      else
210        return UV_EINVAL;
211    }
212  }
213
214  oldthreadmask = SetThreadAffinityMask(*tid, threadmask);
215  if (oldthreadmask == 0)
216    return uv_translate_sys_error(GetLastError());
217
218  if (oldmask != NULL) {
219    for (i = 0; i < cpumasksize; i++)
220      oldmask[i] = (oldthreadmask >> i) & 1;
221  }
222
223  return 0;
224}
225
226int uv_thread_getaffinity(uv_thread_t* tid,
227                          char* cpumask,
228                          size_t mask_size) {
229  int i;
230  HANDLE hproc;
231  DWORD_PTR procmask;
232  DWORD_PTR sysmask;
233  DWORD_PTR threadmask;
234  int cpumasksize;
235
236  cpumasksize = uv_cpumask_size();
237  assert(cpumasksize > 0);
238  if (mask_size < (size_t)cpumasksize)
239    return UV_EINVAL;
240
241  hproc = GetCurrentProcess();
242  if (!GetProcessAffinityMask(hproc, &procmask, &sysmask))
243    return uv_translate_sys_error(GetLastError());
244
245  threadmask = SetThreadAffinityMask(*tid, procmask);
246  if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0)
247    return uv_translate_sys_error(GetLastError());
248
249  for (i = 0; i < cpumasksize; i++)
250    cpumask[i] = (threadmask >> i) & 1;
251
252  return 0;
253}
254
255int uv_thread_getcpu(void) {
256  return GetCurrentProcessorNumber();
257}
258
259uv_thread_t uv_thread_self(void) {
260  uv_thread_t key;
261  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
262  key = uv_key_get(&uv__current_thread_key);
263  if (key == NULL) {
264      /* If the thread wasn't started by uv_thread_create (such as the main
265       * thread), we assign an id to it now. */
266      if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
267                           GetCurrentProcess(), &key, 0,
268                           FALSE, DUPLICATE_SAME_ACCESS)) {
269          uv_fatal_error(GetLastError(), "DuplicateHandle");
270      }
271      uv_key_set(&uv__current_thread_key, key);
272  }
273  return key;
274}
275
276
277int uv_thread_join(uv_thread_t *tid) {
278  if (WaitForSingleObject(*tid, INFINITE))
279    return uv_translate_sys_error(GetLastError());
280  else {
281    CloseHandle(*tid);
282    *tid = 0;
283    MemoryBarrier();  /* For feature parity with pthread_join(). */
284    return 0;
285  }
286}
287
288
289int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
290  return *t1 == *t2;
291}
292
293
294int uv_mutex_init(uv_mutex_t* mutex) {
295  InitializeCriticalSection(mutex);
296  return 0;
297}
298
299
300int uv_mutex_init_recursive(uv_mutex_t* mutex) {
301  return uv_mutex_init(mutex);
302}
303
304
305void uv_mutex_destroy(uv_mutex_t* mutex) {
306  DeleteCriticalSection(mutex);
307}
308
309
310void uv_mutex_lock(uv_mutex_t* mutex) {
311  EnterCriticalSection(mutex);
312}
313
314
315int uv_mutex_trylock(uv_mutex_t* mutex) {
316  if (TryEnterCriticalSection(mutex))
317    return 0;
318  else
319    return UV_EBUSY;
320}
321
322
323void uv_mutex_unlock(uv_mutex_t* mutex) {
324  LeaveCriticalSection(mutex);
325}
326
327/* Ensure that the ABI for this type remains stable in v1.x */
328#ifdef _WIN64
329STATIC_ASSERT(sizeof(uv_rwlock_t) == 80);
330#else
331STATIC_ASSERT(sizeof(uv_rwlock_t) == 48);
332#endif
333
334int uv_rwlock_init(uv_rwlock_t* rwlock) {
335  memset(rwlock, 0, sizeof(*rwlock));
336  InitializeSRWLock(&rwlock->read_write_lock_);
337
338  return 0;
339}
340
341
342void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
343  /* SRWLock does not need explicit destruction so long as there are no waiting threads
344     See: https://docs.microsoft.com/windows/win32/api/synchapi/nf-synchapi-initializesrwlock#remarks */
345}
346
347
348void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
349  AcquireSRWLockShared(&rwlock->read_write_lock_);
350}
351
352
353int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
354  if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_))
355    return UV_EBUSY;
356
357  return 0;
358}
359
360
361void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
362  ReleaseSRWLockShared(&rwlock->read_write_lock_);
363}
364
365
366void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
367  AcquireSRWLockExclusive(&rwlock->read_write_lock_);
368}
369
370
371int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
372  if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_))
373    return UV_EBUSY;
374
375  return 0;
376}
377
378
379void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
380  ReleaseSRWLockExclusive(&rwlock->read_write_lock_);
381}
382
383
384int uv_sem_init(uv_sem_t* sem, unsigned int value) {
385  *sem = CreateSemaphore(NULL, value, INT_MAX, NULL);
386  if (*sem == NULL)
387    return uv_translate_sys_error(GetLastError());
388  else
389    return 0;
390}
391
392
393void uv_sem_destroy(uv_sem_t* sem) {
394  if (!CloseHandle(*sem))
395    abort();
396}
397
398
399void uv_sem_post(uv_sem_t* sem) {
400  if (!ReleaseSemaphore(*sem, 1, NULL))
401    abort();
402}
403
404
405void uv_sem_wait(uv_sem_t* sem) {
406  if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0)
407    abort();
408}
409
410
411int uv_sem_trywait(uv_sem_t* sem) {
412  DWORD r = WaitForSingleObject(*sem, 0);
413
414  if (r == WAIT_OBJECT_0)
415    return 0;
416
417  if (r == WAIT_TIMEOUT)
418    return UV_EAGAIN;
419
420  abort();
421  return -1; /* Satisfy the compiler. */
422}
423
424
425int uv_cond_init(uv_cond_t* cond) {
426  InitializeConditionVariable(&cond->cond_var);
427  return 0;
428}
429
430
431void uv_cond_destroy(uv_cond_t* cond) {
432  /* nothing to do */
433  (void) &cond;
434}
435
436
437void uv_cond_signal(uv_cond_t* cond) {
438  WakeConditionVariable(&cond->cond_var);
439}
440
441
442void uv_cond_broadcast(uv_cond_t* cond) {
443  WakeAllConditionVariable(&cond->cond_var);
444}
445
446
447void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
448  if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
449    abort();
450}
451
452
453int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
454  if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
455    return 0;
456  if (GetLastError() != ERROR_TIMEOUT)
457    abort();
458  return UV_ETIMEDOUT;
459}
460
461
462int uv_key_create(uv_key_t* key) {
463  key->tls_index = TlsAlloc();
464  if (key->tls_index == TLS_OUT_OF_INDEXES)
465    return UV_ENOMEM;
466  return 0;
467}
468
469
470void uv_key_delete(uv_key_t* key) {
471  if (TlsFree(key->tls_index) == FALSE)
472    abort();
473  key->tls_index = TLS_OUT_OF_INDEXES;
474}
475
476
477void* uv_key_get(uv_key_t* key) {
478  void* value;
479
480  value = TlsGetValue(key->tls_index);
481  if (value == NULL)
482    if (GetLastError() != ERROR_SUCCESS)
483      abort();
484
485  return value;
486}
487
488
489void uv_key_set(uv_key_t* key, void* value) {
490  if (TlsSetValue(key->tls_index, value) == FALSE)
491    abort();
492}
493