1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2017 Joyent, Inc.
14 */
15
16 #include <pthread.h>
17 #include <thread.h>
18 #include <synch.h>
19 #include <threads.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23
24 /*
25 * ISO/IEC C11 thread support.
26 *
27 * In illumos, the underlying implementation of lock related routines is the
28 * same between pthreads and traditional SunOS routines. The same is true with
29 * the C11 routines. Their types are actually just typedef's to other things.
30 * Thus in the implementation here, we treat this as a wrapper around existing
31 * thread related routines and don't sweet the extra indirection.
32 *
33 * Note that in many places the C standard doesn't allow for errors to be
34 * returned. In those cases, if we have an instance of programmer error
35 * (something resulting in EINVAL), we opt to abort the program as we don't have
36 * much other recourse available.
37 */
38
39 void
call_once(once_flag * flag,void (* func)(void))40 call_once(once_flag *flag, void (*func)(void))
41 {
42 if (pthread_once(flag, func) != 0)
43 abort();
44 }
45
46 int
cnd_broadcast(cnd_t * cnd)47 cnd_broadcast(cnd_t *cnd)
48 {
49 int ret;
50
51 ret = pthread_cond_broadcast(cnd);
52 if (ret == 0)
53 return (thrd_success);
54 else
55 return (thrd_error);
56 }
57
58 void
cnd_destroy(cnd_t * cnd)59 cnd_destroy(cnd_t *cnd)
60 {
61 if (pthread_cond_destroy(cnd) != 0)
62 abort();
63 }
64
65 int
cnd_init(cnd_t * cnd)66 cnd_init(cnd_t *cnd)
67 {
68 int ret;
69
70 ret = pthread_cond_init(cnd, NULL);
71 if (ret == 0)
72 return (thrd_success);
73 return (thrd_error);
74 }
75
76 int
cnd_signal(cnd_t * cnd)77 cnd_signal(cnd_t *cnd)
78 {
79 int ret;
80
81 ret = pthread_cond_signal(cnd);
82 if (ret == 0)
83 return (thrd_success);
84 else
85 return (thrd_error);
86 }
87
88 /* ARGSUSED */
89 int
cnd_timedwait(cnd_t * _RESTRICT_KYWD cnd,mtx_t * _RESTRICT_KYWD mtx,const struct timespec * _RESTRICT_KYWD ts)90 cnd_timedwait(cnd_t *_RESTRICT_KYWD cnd, mtx_t *_RESTRICT_KYWD mtx,
91 const struct timespec *_RESTRICT_KYWD ts)
92 {
93 int ret;
94
95 ret = pthread_cond_timedwait(cnd, mtx, ts);
96 if (ret == 0)
97 return (thrd_success);
98 if (ret == ETIMEDOUT)
99 return (thrd_timedout);
100 return (thrd_error);
101 }
102
103 /* ARGSUSED */
104 int
cnd_wait(cnd_t * cnd,mtx_t * mtx)105 cnd_wait(cnd_t *cnd, mtx_t *mtx)
106 {
107 int ret;
108
109 ret = pthread_cond_wait(cnd, mtx);
110 if (ret == 0)
111 return (thrd_success);
112 return (thrd_error);
113 }
114
115 void
mtx_destroy(mtx_t * mtx)116 mtx_destroy(mtx_t *mtx)
117 {
118 if (pthread_mutex_destroy(mtx) != 0)
119 abort();
120 }
121
122 int
mtx_init(mtx_t * mtx,int type)123 mtx_init(mtx_t *mtx, int type)
124 {
125 int mtype;
126
127 switch (type) {
128 case mtx_plain:
129 case mtx_timed:
130 mtype = USYNC_THREAD;
131 break;
132 case mtx_plain | mtx_recursive:
133 case mtx_timed | mtx_recursive:
134 mtype = USYNC_THREAD | LOCK_RECURSIVE;
135 break;
136 default:
137 return (thrd_error);
138 }
139
140 /*
141 * Here, we buck the trend and use the traditional SunOS routine. It's
142 * much simpler than fighting with pthread attributes.
143 */
144 if (mutex_init((mutex_t *)mtx, mtype, NULL) == 0)
145 return (thrd_success);
146 return (thrd_error);
147 }
148
149 int
mtx_lock(mtx_t * mtx)150 mtx_lock(mtx_t *mtx)
151 {
152 if (pthread_mutex_lock(mtx) == 0)
153 return (thrd_success);
154 return (thrd_error);
155 }
156
157 int
mtx_timedlock(mtx_t * _RESTRICT_KYWD mtx,const struct timespec * _RESTRICT_KYWD abstime)158 mtx_timedlock(mtx_t *_RESTRICT_KYWD mtx,
159 const struct timespec *_RESTRICT_KYWD abstime)
160 {
161 int ret;
162
163 ret = pthread_mutex_timedlock(mtx, abstime);
164 if (ret == ETIMEDOUT)
165 return (thrd_timedout);
166 else if (ret != 0)
167 return (thrd_error);
168 return (thrd_success);
169 }
170
171 int
mtx_trylock(mtx_t * mtx)172 mtx_trylock(mtx_t *mtx)
173 {
174 int ret;
175
176 ret = pthread_mutex_trylock(mtx);
177 if (ret == 0)
178 return (thrd_success);
179 else if (ret == EBUSY)
180 return (thrd_busy);
181 else
182 return (thrd_error);
183 }
184
185 int
mtx_unlock(mtx_t * mtx)186 mtx_unlock(mtx_t *mtx)
187 {
188 if (pthread_mutex_unlock(mtx) == 0)
189 return (thrd_success);
190 return (thrd_error);
191 }
192
193 int
thrd_create(thrd_t * thr,thrd_start_t func,void * arg)194 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
195 {
196 int ret;
197
198 ret = pthread_create(thr, NULL,
199 (void *(*)(void *))(uintptr_t)func, arg);
200 if (ret == 0)
201 return (thrd_success);
202 else if (ret == -1 && errno == EAGAIN)
203 return (thrd_nomem);
204 else
205 return (thrd_error);
206 }
207
208 thrd_t
thrd_current(void)209 thrd_current(void)
210 {
211 return (pthread_self());
212 }
213
214 int
thrd_detach(thrd_t thr)215 thrd_detach(thrd_t thr)
216 {
217 if (pthread_detach(thr) == 0)
218 return (thrd_success);
219 return (thrd_error);
220 }
221
222 int
thrd_equal(thrd_t t1,thrd_t t2)223 thrd_equal(thrd_t t1, thrd_t t2)
224 {
225 return (pthread_equal(t1, t2));
226 }
227
228 _NORETURN_KYWD void
thrd_exit(int res)229 thrd_exit(int res)
230 {
231 pthread_exit((void *)(uintptr_t)res);
232 }
233
234 int
thrd_join(thrd_t thrd,int * res)235 thrd_join(thrd_t thrd, int *res)
236 {
237 void *es;
238
239 if (pthread_join(thrd, &es) != 0)
240 return (thrd_error);
241 if (res != NULL)
242 *res = (uintptr_t)es;
243 return (thrd_success);
244 }
245
246 /*
247 * thrd_sleep has somewhat odd standardized return values. It doesn't use the
248 * same returns values as the thrd_* family of functions at all.
249 */
250 int
thrd_sleep(const struct timespec * rqtp,struct timespec * rmtp)251 thrd_sleep(const struct timespec *rqtp, struct timespec *rmtp)
252 {
253 int ret;
254 if ((ret = nanosleep(rqtp, rmtp)) == 0)
255 return (0);
256 if (ret == -1 && errno == EINTR)
257 return (-1);
258 return (-2);
259 }
260
261 void
thrd_yield(void)262 thrd_yield(void)
263 {
264 thr_yield();
265 }
266
267 int
tss_create(tss_t * key,tss_dtor_t dtor)268 tss_create(tss_t *key, tss_dtor_t dtor)
269 {
270 if (pthread_key_create(key, dtor) == 0)
271 return (thrd_success);
272 return (thrd_error);
273 }
274
275 void
tss_delete(tss_t key)276 tss_delete(tss_t key)
277 {
278 if (pthread_key_delete(key) != 0)
279 abort();
280 }
281
282 void *
tss_get(tss_t key)283 tss_get(tss_t key)
284 {
285 return (pthread_getspecific(key));
286 }
287
288 int
tss_set(tss_t key,void * val)289 tss_set(tss_t key, void *val)
290 {
291 if (pthread_setspecific(key, val) == 0)
292 return (thrd_success);
293 return (thrd_error);
294 }
295