1 /*
2 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 /*
8 * Native Client condition variable API
9 */
10
11 #include <errno.h>
12 #include <limits.h>
13 #include <unistd.h>
14
15 #include "native_client/src/untrusted/nacl/nacl_irt.h"
16 #include "native_client/src/untrusted/pthread/pthread.h"
17 #include "native_client/src/untrusted/pthread/pthread_internal.h"
18 #include "native_client/src/untrusted/pthread/pthread_types.h"
19
20 /*
21 * This implementation is based on the condvar implementation in
22 * Android's C library, Bionic. See libc/bionic/pthread.c in Bionic:
23 *
24 * https://android.googlesource.com/platform/bionic.git/+/7a34ed2bb36fcbe6967d8b670f4d70ada1dcef49/libc/bionic/pthread.c
25 *
26 * Technically there is a race condition in our pthread_cond_wait():
27 * It could miss a wakeup if pthread_cond_signal() or
28 * pthread_cond_broadcast() is called an exact multiple of 2^32 times
29 * between pthread_cond_wait()'s fetching of cond->sequence_number and
30 * its call to futex_wait(). That is very unlikely to happen,
31 * however.
32 *
33 * Unlike glibc's more complex condvar implementation, we do not
34 * attempt to optimize pthread_cond_signal/broadcast() to avoid a
35 * futex_wake() call in the case where there are no waiting threads.
36 */
37
38
39 /*
40 * Initialize condition variable COND using attributes ATTR, or use
41 * the default values if later is NULL.
42 */
pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * cond_attr)43 int pthread_cond_init(pthread_cond_t *cond,
44 const pthread_condattr_t *cond_attr) {
45 cond->sequence_number = 0;
46 return 0;
47 }
48
49 /*
50 * Destroy condition variable COND.
51 */
pthread_cond_destroy(pthread_cond_t * cond)52 int pthread_cond_destroy(pthread_cond_t *cond) {
53 return 0;
54 }
55
pulse(pthread_cond_t * cond,int count)56 static int pulse(pthread_cond_t *cond, int count) {
57 /*
58 * This atomic increment executes the full memory barrier that
59 * pthread_cond_signal/broadcast() are required to execute.
60 */
61 __sync_fetch_and_add(&cond->sequence_number, 1);
62
63 int unused_woken_count;
64 __libnacl_irt_futex.futex_wake(&cond->sequence_number, count,
65 &unused_woken_count);
66 return 0;
67 }
68
69 /*
70 * Wake up one thread waiting for condition variable COND.
71 */
pthread_cond_signal(pthread_cond_t * cond)72 int pthread_cond_signal(pthread_cond_t *cond) {
73 return pulse(cond, 1);
74 }
75
pthread_cond_broadcast(pthread_cond_t * cond)76 int pthread_cond_broadcast(pthread_cond_t *cond) {
77 return pulse(cond, INT_MAX);
78 }
79
pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)80 int pthread_cond_wait(pthread_cond_t *cond,
81 pthread_mutex_t *mutex) {
82 return pthread_cond_timedwait_abs(cond, mutex, NULL);
83 }
84
pthread_cond_timedwait_abs(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)85 int pthread_cond_timedwait_abs(pthread_cond_t *cond,
86 pthread_mutex_t *mutex,
87 const struct timespec *abstime) {
88 int old_value = cond->sequence_number;
89
90 int err = pthread_mutex_unlock(mutex);
91 if (err != 0)
92 return err;
93
94 int status = __libnacl_irt_futex.futex_wait_abs(&cond->sequence_number,
95 old_value, abstime);
96
97 err = pthread_mutex_lock(mutex);
98 if (err != 0)
99 return err;
100
101 /*
102 * futex_wait() can return EWOULDBLOCK but pthread_cond_wait() is
103 * not allowed to return that.
104 */
105 if (status == ETIMEDOUT) {
106 return ETIMEDOUT;
107 } else {
108 return 0;
109 }
110 }
111
pthread_condattr_init(pthread_condattr_t * attr)112 int pthread_condattr_init(pthread_condattr_t *attr) {
113 return 0;
114 }
115
pthread_condattr_destroy(pthread_condattr_t * attr)116 int pthread_condattr_destroy(pthread_condattr_t *attr) {
117 return 0;
118 }
119
pthread_condattr_getpshared(const pthread_condattr_t * attr,int * pshared)120 int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
121 *pshared = PTHREAD_PROCESS_PRIVATE;
122 return 0;
123 }
124
pthread_condattr_setpshared(pthread_condattr_t * attr,int pshared)125 int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
126 switch (pshared) {
127 case PTHREAD_PROCESS_PRIVATE:
128 return 0;
129 case PTHREAD_PROCESS_SHARED:
130 return ENOTSUP;
131 default:
132 return EINVAL;
133 }
134 }
135
pthread_condattr_getclock(const pthread_condattr_t * attr,clockid_t * clock_id)136 int pthread_condattr_getclock(const pthread_condattr_t *attr,
137 clockid_t *clock_id) {
138 *clock_id = CLOCK_REALTIME;
139 return 0;
140 }
141
pthread_condattr_setclock(pthread_condattr_t * attr,clockid_t clock_id)142 int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) {
143 switch (clock_id) {
144 case CLOCK_REALTIME: /* Only one really supported. */
145 return 0;
146
147 case CLOCK_MONOTONIC: /* A "known clock", but not supported for this. */
148 return ENOTSUP;
149
150 default:
151 /*
152 * Anything else is either not a "known clock", or is a CPU-time clock.
153 */
154 return EINVAL;
155 }
156 }
157