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 "uv.h"
23#include "internal.h"
24
25#include <pthread.h>
26#include <assert.h>
27#include <errno.h>
28
29#include <sys/time.h>
30#include <sys/resource.h> /* getrlimit() */
31#include <unistd.h> /* getpagesize() */
32
33#include <limits.h>
34
35#ifdef __MVS__
36#include <sys/ipc.h>
37#include <sys/sem.h>
38#endif
39
40#if defined(__GLIBC__) && !defined(__UCLIBC__)
41#include <gnu/libc-version.h> /* gnu_get_libc_version() */
42#endif
43
44#undef NANOSEC
45#define NANOSEC ((uint64_t) 1e9)
46
47#if defined(PTHREAD_BARRIER_SERIAL_THREAD)
48STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
49#endif
50
51/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
52#if defined(_AIX) || \
53 defined(__OpenBSD__) || \
54 !defined(PTHREAD_BARRIER_SERIAL_THREAD)
55int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
56 struct _uv_barrier* b;
57 int rc;
58
59 if (barrier == NULL || count == 0)
60 return UV_EINVAL;
61
62 b = uv__malloc(sizeof(*b));
63 if (b == NULL)
64 return UV_ENOMEM;
65
66 b->in = 0;
67 b->out = 0;
68 b->threshold = count;
69
70 rc = uv_mutex_init(&b->mutex);
71 if (rc != 0)
72 goto error2;
73
74 rc = uv_cond_init(&b->cond);
75 if (rc != 0)
76 goto error;
77
78 barrier->b = b;
79 return 0;
80
81error:
82 uv_mutex_destroy(&b->mutex);
83error2:
84 uv__free(b);
85 return rc;
86}
87
88
89int uv_barrier_wait(uv_barrier_t* barrier) {
90 struct _uv_barrier* b;
91 int last;
92
93 if (barrier == NULL || barrier->b == NULL)
94 return UV_EINVAL;
95
96 b = barrier->b;
97 uv_mutex_lock(&b->mutex);
98
99 if (++b->in == b->threshold) {
100 b->in = 0;
101 b->out = b->threshold;
102 uv_cond_signal(&b->cond);
103 } else {
104 do
105 uv_cond_wait(&b->cond, &b->mutex);
106 while (b->in != 0);
107 }
108
109 last = (--b->out == 0);
110 if (!last)
111 uv_cond_signal(&b->cond); /* Not needed for last thread. */
112
113 uv_mutex_unlock(&b->mutex);
114 return last;
115}
116
117
118void uv_barrier_destroy(uv_barrier_t* barrier) {
119 struct _uv_barrier* b;
120
121 b = barrier->b;
122 uv_mutex_lock(&b->mutex);
123
124 assert(b->in == 0);
125 assert(b->out == 0);
126
127 if (b->in != 0 || b->out != 0)
128 abort();
129
130 uv_mutex_unlock(&b->mutex);
131 uv_mutex_destroy(&b->mutex);
132 uv_cond_destroy(&b->cond);
133
134 uv__free(barrier->b);
135 barrier->b = NULL;
136}
137
138#else
139
140int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
141 return UV__ERR(pthread_barrier_init(barrier, NULL, count));
142}
143
144
145int uv_barrier_wait(uv_barrier_t* barrier) {
146 int rc;
147
148 rc = pthread_barrier_wait(barrier);
149 if (rc != 0)
150 if (rc != PTHREAD_BARRIER_SERIAL_THREAD)
151 abort();
152
153 return rc == PTHREAD_BARRIER_SERIAL_THREAD;
154}
155
156
157void uv_barrier_destroy(uv_barrier_t* barrier) {
158 if (pthread_barrier_destroy(barrier))
159 abort();
160}
161
162#endif
163
164
165/* On MacOS, threads other than the main thread are created with a reduced
166 * stack size by default. Adjust to RLIMIT_STACK aligned to the page size.
167 *
168 * On Linux, threads created by musl have a much smaller stack than threads
169 * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency.
170 */
171static size_t thread_stack_size(void) {
172#if defined(__APPLE__) || defined(__linux__)
173 struct rlimit lim;
174
175 /* getrlimit() can fail on some aarch64 systems due to a glibc bug where
176 * the system call wrapper invokes the wrong system call. Don't treat
177 * that as fatal, just use the default stack size instead.
178 */
179 if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) {
180 /* pthread_attr_setstacksize() expects page-aligned values. */
181 lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
182
183 /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
184 * too small to safely receive signals on.
185 *
186 * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
187 * the largest MINSIGSTKSZ of the architectures that musl supports) so
188 * let's use that as a lower bound.
189 *
190 * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
191 * is between 28 and 133 KB when compiling against glibc, depending
192 * on the architecture.
193 */
194 if (lim.rlim_cur >= 8192)
195 if (lim.rlim_cur >= PTHREAD_STACK_MIN)
196 return lim.rlim_cur;
197 }
198#endif
199
200#if !defined(__linux__)
201 return 0;
202#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)
203 return 4 << 20; /* glibc default. */
204#else
205 return 2 << 20; /* glibc default. */
206#endif
207}
208
209
210int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
211 uv_thread_options_t params;
212 params.flags = UV_THREAD_NO_FLAGS;
213 return uv_thread_create_ex(tid, &params, entry, arg);
214}
215
216int uv_thread_create_ex(uv_thread_t* tid,
217 const uv_thread_options_t* params,
218 void (*entry)(void *arg),
219 void *arg) {
220 int err;
221 pthread_attr_t* attr;
222 pthread_attr_t attr_storage;
223 size_t pagesize;
224 size_t stack_size;
225
226 /* Used to squelch a -Wcast-function-type warning. */
227 union {
228 void (*in)(void*);
229 void* (*out)(void*);
230 } f;
231
232 stack_size =
233 params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
234
235 attr = NULL;
236 if (stack_size == 0) {
237 stack_size = thread_stack_size();
238 } else {
239 pagesize = (size_t)getpagesize();
240 /* Round up to the nearest page boundary. */
241 stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
242#ifdef PTHREAD_STACK_MIN
243 if (stack_size < PTHREAD_STACK_MIN)
244 stack_size = PTHREAD_STACK_MIN;
245#endif
246 }
247
248 if (stack_size > 0) {
249 attr = &attr_storage;
250
251 if (pthread_attr_init(attr))
252 abort();
253
254 if (pthread_attr_setstacksize(attr, stack_size))
255 abort();
256 }
257
258 f.in = entry;
259 err = pthread_create(tid, attr, f.out, arg);
260
261 if (attr != NULL)
262 pthread_attr_destroy(attr);
263
264 return UV__ERR(err);
265}
266
267
268uv_thread_t uv_thread_self(void) {
269 return pthread_self();
270}
271
272int uv_thread_join(uv_thread_t *tid) {
273 return UV__ERR(pthread_join(*tid, NULL));
274}
275
276
277int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
278 return pthread_equal(*t1, *t2);
279}
280
281
282int uv_mutex_init(uv_mutex_t* mutex) {
283#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
284 return UV__ERR(pthread_mutex_init(mutex, NULL));
285#else
286 pthread_mutexattr_t attr;
287 int err;
288
289 if (pthread_mutexattr_init(&attr))
290 abort();
291
292 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
293 abort();
294
295 err = pthread_mutex_init(mutex, &attr);
296
297 if (pthread_mutexattr_destroy(&attr))
298 abort();
299
300 return UV__ERR(err);
301#endif
302}
303
304
305int uv_mutex_init_recursive(uv_mutex_t* mutex) {
306 pthread_mutexattr_t attr;
307 int err;
308
309 if (pthread_mutexattr_init(&attr))
310 abort();
311
312 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))
313 abort();
314
315 err = pthread_mutex_init(mutex, &attr);
316
317 if (pthread_mutexattr_destroy(&attr))
318 abort();
319
320 return UV__ERR(err);
321}
322
323
324void uv_mutex_destroy(uv_mutex_t* mutex) {
325 if (pthread_mutex_destroy(mutex))
326 abort();
327}
328
329
330void uv_mutex_lock(uv_mutex_t* mutex) {
331 if (pthread_mutex_lock(mutex))
332 abort();
333}
334
335
336int uv_mutex_trylock(uv_mutex_t* mutex) {
337 int err;
338
339 err = pthread_mutex_trylock(mutex);
340 if (err) {
341 if (err != EBUSY && err != EAGAIN)
342 abort();
343 return UV_EBUSY;
344 }
345
346 return 0;
347}
348
349
350void uv_mutex_unlock(uv_mutex_t* mutex) {
351 if (pthread_mutex_unlock(mutex))
352 abort();
353}
354
355
356int uv_rwlock_init(uv_rwlock_t* rwlock) {
357 return UV__ERR(pthread_rwlock_init(rwlock, NULL));
358}
359
360
361void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
362 if (pthread_rwlock_destroy(rwlock))
363 abort();
364}
365
366
367void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
368 if (pthread_rwlock_rdlock(rwlock))
369 abort();
370}
371
372
373int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
374 int err;
375
376 err = pthread_rwlock_tryrdlock(rwlock);
377 if (err) {
378 if (err != EBUSY && err != EAGAIN)
379 abort();
380 return UV_EBUSY;
381 }
382
383 return 0;
384}
385
386
387void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
388 if (pthread_rwlock_unlock(rwlock))
389 abort();
390}
391
392
393void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
394 if (pthread_rwlock_wrlock(rwlock))
395 abort();
396}
397
398
399int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
400 int err;
401
402 err = pthread_rwlock_trywrlock(rwlock);
403 if (err) {
404 if (err != EBUSY && err != EAGAIN)
405 abort();
406 return UV_EBUSY;
407 }
408
409 return 0;
410}
411
412
413void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
414 if (pthread_rwlock_unlock(rwlock))
415 abort();
416}
417
418
419void uv_once(uv_once_t* guard, void (*callback)(void)) {
420 if (pthread_once(guard, callback))
421 abort();
422}
423
424#if defined(__APPLE__) && defined(__MACH__)
425
426int uv_sem_init(uv_sem_t* sem, unsigned int value) {
427 kern_return_t err;
428
429 err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
430 if (err == KERN_SUCCESS)
431 return 0;
432 if (err == KERN_INVALID_ARGUMENT)
433 return UV_EINVAL;
434 if (err == KERN_RESOURCE_SHORTAGE)
435 return UV_ENOMEM;
436
437 abort();
438 return UV_EINVAL; /* Satisfy the compiler. */
439}
440
441
442void uv_sem_destroy(uv_sem_t* sem) {
443 if (semaphore_destroy(mach_task_self(), *sem))
444 abort();
445}
446
447
448void uv_sem_post(uv_sem_t* sem) {
449 if (semaphore_signal(*sem))
450 abort();
451}
452
453
454void uv_sem_wait(uv_sem_t* sem) {
455 int r;
456
457 do
458 r = semaphore_wait(*sem);
459 while (r == KERN_ABORTED);
460
461 if (r != KERN_SUCCESS)
462 abort();
463}
464
465
466int uv_sem_trywait(uv_sem_t* sem) {
467 mach_timespec_t interval;
468 kern_return_t err;
469
470 interval.tv_sec = 0;
471 interval.tv_nsec = 0;
472
473 err = semaphore_timedwait(*sem, interval);
474 if (err == KERN_SUCCESS)
475 return 0;
476 if (err == KERN_OPERATION_TIMED_OUT)
477 return UV_EAGAIN;
478
479 abort();
480 return UV_EINVAL; /* Satisfy the compiler. */
481}
482
483#else /* !(defined(__APPLE__) && defined(__MACH__)) */
484
485#if defined(__GLIBC__) && !defined(__UCLIBC__)
486
487/* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674
488 * by providing a custom implementation for glibc < 2.21 in terms of other
489 * concurrency primitives.
490 * Refs: https://github.com/nodejs/node/issues/19903 */
491
492/* To preserve ABI compatibility, we treat the uv_sem_t as storage for
493 * a pointer to the actual struct we're using underneath. */
494
495static uv_once_t glibc_version_check_once = UV_ONCE_INIT;
496static int platform_needs_custom_semaphore = 0;
497
498static void glibc_version_check(void) {
499 const char* version = gnu_get_libc_version();
500 platform_needs_custom_semaphore =
501 version[0] == '2' && version[1] == '.' &&
502 atoi(version + 2) < 21;
503}
504
505#elif defined(__MVS__)
506
507#define platform_needs_custom_semaphore 1
508
509#else /* !defined(__GLIBC__) && !defined(__MVS__) */
510
511#define platform_needs_custom_semaphore 0
512
513#endif
514
515typedef struct uv_semaphore_s {
516 uv_mutex_t mutex;
517 uv_cond_t cond;
518 unsigned int value;
519} uv_semaphore_t;
520
521#if (defined(__GLIBC__) && !defined(__UCLIBC__)) || \
522 platform_needs_custom_semaphore
523STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*));
524#endif
525
526static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) {
527 int err;
528 uv_semaphore_t* sem;
529
530 sem = uv__malloc(sizeof(*sem));
531 if (sem == NULL)
532 return UV_ENOMEM;
533
534 if ((err = uv_mutex_init(&sem->mutex)) != 0) {
535 uv__free(sem);
536 return err;
537 }
538
539 if ((err = uv_cond_init(&sem->cond)) != 0) {
540 uv_mutex_destroy(&sem->mutex);
541 uv__free(sem);
542 return err;
543 }
544
545 sem->value = value;
546 *(uv_semaphore_t**)sem_ = sem;
547 return 0;
548}
549
550
551static void uv__custom_sem_destroy(uv_sem_t* sem_) {
552 uv_semaphore_t* sem;
553
554 sem = *(uv_semaphore_t**)sem_;
555 uv_cond_destroy(&sem->cond);
556 uv_mutex_destroy(&sem->mutex);
557 uv__free(sem);
558}
559
560
561static void uv__custom_sem_post(uv_sem_t* sem_) {
562 uv_semaphore_t* sem;
563
564 sem = *(uv_semaphore_t**)sem_;
565 uv_mutex_lock(&sem->mutex);
566 sem->value++;
567 if (sem->value == 1)
568 uv_cond_signal(&sem->cond);
569 uv_mutex_unlock(&sem->mutex);
570}
571
572
573static void uv__custom_sem_wait(uv_sem_t* sem_) {
574 uv_semaphore_t* sem;
575
576 sem = *(uv_semaphore_t**)sem_;
577 uv_mutex_lock(&sem->mutex);
578 while (sem->value == 0)
579 uv_cond_wait(&sem->cond, &sem->mutex);
580 sem->value--;
581 uv_mutex_unlock(&sem->mutex);
582}
583
584
585static int uv__custom_sem_trywait(uv_sem_t* sem_) {
586 uv_semaphore_t* sem;
587
588 sem = *(uv_semaphore_t**)sem_;
589 if (uv_mutex_trylock(&sem->mutex) != 0)
590 return UV_EAGAIN;
591
592 if (sem->value == 0) {
593 uv_mutex_unlock(&sem->mutex);
594 return UV_EAGAIN;
595 }
596
597 sem->value--;
598 uv_mutex_unlock(&sem->mutex);
599
600 return 0;
601}
602
603static int uv__sem_init(uv_sem_t* sem, unsigned int value) {
604 if (sem_init(sem, 0, value))
605 return UV__ERR(errno);
606 return 0;
607}
608
609
610static void uv__sem_destroy(uv_sem_t* sem) {
611 if (sem_destroy(sem))
612 abort();
613}
614
615
616static void uv__sem_post(uv_sem_t* sem) {
617 if (sem_post(sem))
618 abort();
619}
620
621
622static void uv__sem_wait(uv_sem_t* sem) {
623 int r;
624
625 do
626 r = sem_wait(sem);
627 while (r == -1 && errno == EINTR);
628
629 if (r)
630 abort();
631}
632
633
634static int uv__sem_trywait(uv_sem_t* sem) {
635 int r;
636
637 do
638 r = sem_trywait(sem);
639 while (r == -1 && errno == EINTR);
640
641 if (r) {
642 if (errno == EAGAIN)
643 return UV_EAGAIN;
644 abort();
645 }
646
647 return 0;
648}
649
650int uv_sem_init(uv_sem_t* sem, unsigned int value) {
651#if defined(__GLIBC__) && !defined(__UCLIBC__)
652 uv_once(&glibc_version_check_once, glibc_version_check);
653#endif
654
655 if (platform_needs_custom_semaphore)
656 return uv__custom_sem_init(sem, value);
657 else
658 return uv__sem_init(sem, value);
659}
660
661
662void uv_sem_destroy(uv_sem_t* sem) {
663 if (platform_needs_custom_semaphore)
664 uv__custom_sem_destroy(sem);
665 else
666 uv__sem_destroy(sem);
667}
668
669
670void uv_sem_post(uv_sem_t* sem) {
671 if (platform_needs_custom_semaphore)
672 uv__custom_sem_post(sem);
673 else
674 uv__sem_post(sem);
675}
676
677
678void uv_sem_wait(uv_sem_t* sem) {
679 if (platform_needs_custom_semaphore)
680 uv__custom_sem_wait(sem);
681 else
682 uv__sem_wait(sem);
683}
684
685
686int uv_sem_trywait(uv_sem_t* sem) {
687 if (platform_needs_custom_semaphore)
688 return uv__custom_sem_trywait(sem);
689 else
690 return uv__sem_trywait(sem);
691}
692
693#endif /* defined(__APPLE__) && defined(__MACH__) */
694
695
696#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)
697
698int uv_cond_init(uv_cond_t* cond) {
699 return UV__ERR(pthread_cond_init(cond, NULL));
700}
701
702#else /* !(defined(__APPLE__) && defined(__MACH__)) */
703
704int uv_cond_init(uv_cond_t* cond) {
705 pthread_condattr_t attr;
706 int err;
707
708 err = pthread_condattr_init(&attr);
709 if (err)
710 return UV__ERR(err);
711
712 err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
713 if (err)
714 goto error2;
715
716 err = pthread_cond_init(cond, &attr);
717 if (err)
718 goto error2;
719
720 err = pthread_condattr_destroy(&attr);
721 if (err)
722 goto error;
723
724 return 0;
725
726error:
727 pthread_cond_destroy(cond);
728error2:
729 pthread_condattr_destroy(&attr);
730 return UV__ERR(err);
731}
732
733#endif /* defined(__APPLE__) && defined(__MACH__) */
734
735void uv_cond_destroy(uv_cond_t* cond) {
736#if defined(__APPLE__) && defined(__MACH__)
737 /* It has been reported that destroying condition variables that have been
738 * signalled but not waited on can sometimes result in application crashes.
739 * See https://codereview.chromium.org/1323293005.
740 */
741 pthread_mutex_t mutex;
742 struct timespec ts;
743 int err;
744
745 if (pthread_mutex_init(&mutex, NULL))
746 abort();
747
748 if (pthread_mutex_lock(&mutex))
749 abort();
750
751 ts.tv_sec = 0;
752 ts.tv_nsec = 1;
753
754 err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
755 if (err != 0 && err != ETIMEDOUT)
756 abort();
757
758 if (pthread_mutex_unlock(&mutex))
759 abort();
760
761 if (pthread_mutex_destroy(&mutex))
762 abort();
763#endif /* defined(__APPLE__) && defined(__MACH__) */
764
765 if (pthread_cond_destroy(cond))
766 abort();
767}
768
769void uv_cond_signal(uv_cond_t* cond) {
770 if (pthread_cond_signal(cond))
771 abort();
772}
773
774void uv_cond_broadcast(uv_cond_t* cond) {
775 if (pthread_cond_broadcast(cond))
776 abort();
777}
778
779void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
780 if (pthread_cond_wait(cond, mutex))
781 abort();
782}
783
784
785int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
786 int r;
787 struct timespec ts;
788#if defined(__MVS__)
789 struct timeval tv;
790#endif
791
792#if defined(__APPLE__) && defined(__MACH__)
793 ts.tv_sec = timeout / NANOSEC;
794 ts.tv_nsec = timeout % NANOSEC;
795 r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
796#else
797#if defined(__MVS__)
798 if (gettimeofday(&tv, NULL))
799 abort();
800 timeout += tv.tv_sec * NANOSEC + tv.tv_usec * 1e3;
801#else
802 timeout += uv__hrtime(UV_CLOCK_PRECISE);
803#endif
804 ts.tv_sec = timeout / NANOSEC;
805 ts.tv_nsec = timeout % NANOSEC;
806 r = pthread_cond_timedwait(cond, mutex, &ts);
807#endif
808
809
810 if (r == 0)
811 return 0;
812
813 if (r == ETIMEDOUT)
814 return UV_ETIMEDOUT;
815
816 abort();
817#ifndef __SUNPRO_C
818 return UV_EINVAL; /* Satisfy the compiler. */
819#endif
820}
821
822
823int uv_key_create(uv_key_t* key) {
824 return UV__ERR(pthread_key_create(key, NULL));
825}
826
827
828void uv_key_delete(uv_key_t* key) {
829 if (pthread_key_delete(*key))
830 abort();
831}
832
833
834void* uv_key_get(uv_key_t* key) {
835 return pthread_getspecific(*key);
836}
837
838
839void uv_key_set(uv_key_t* key, void* value) {
840 if (pthread_setspecific(*key, value))
841 abort();
842}
843