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