xref: /illumos-gate/usr/src/lib/libc/port/threads/c11_thr.c (revision 5f82aa32fbc5dc2c59bca6ff315f44a4c4c9ea86)
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
40 call_once(once_flag *flag, void (*func)(void))
41 {
42 	if (pthread_once(flag, func) != 0)
43 		abort();
44 }
45 
46 int
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
59 cnd_destroy(cnd_t *cnd)
60 {
61 	if (pthread_cond_destroy(cnd) != 0)
62 		abort();
63 }
64 
65 int
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
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
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
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
116 mtx_destroy(mtx_t *mtx)
117 {
118 	if (pthread_mutex_destroy(mtx) != 0)
119 		abort();
120 }
121 
122 int
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
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
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
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
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
194 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
195 {
196 	int ret;
197 
198 	ret = pthread_create(thr, NULL, (void *(*)(void *))func, arg);
199 	if (ret == 0)
200 		return (thrd_success);
201 	else if (ret == -1 && errno == EAGAIN)
202 		return (thrd_nomem);
203 	else
204 		return (thrd_error);
205 }
206 
207 thrd_t
208 thrd_current(void)
209 {
210 	return (pthread_self());
211 }
212 
213 int
214 thrd_detach(thrd_t thr)
215 {
216 	if (pthread_detach(thr) == 0)
217 		return (thrd_success);
218 	return (thrd_error);
219 }
220 
221 int
222 thrd_equal(thrd_t t1, thrd_t t2)
223 {
224 	return (pthread_equal(t1, t2));
225 }
226 
227 _NORETURN_KYWD void
228 thrd_exit(int res)
229 {
230 	pthread_exit((void *)(uintptr_t)res);
231 }
232 
233 int
234 thrd_join(thrd_t thrd, int *res)
235 {
236 	void *es;
237 
238 	if (pthread_join(thrd, &es) != 0)
239 		return (thrd_error);
240 	if (res != NULL)
241 		*res = (uintptr_t)es;
242 	return (thrd_success);
243 }
244 
245 /*
246  * thrd_sleep has somewhat odd standardized return values. It doesn't use the
247  * same returns values as the thrd_* family of functions at all.
248  */
249 int
250 thrd_sleep(const struct timespec *rqtp, struct timespec *rmtp)
251 {
252 	int ret;
253 	if ((ret = nanosleep(rqtp, rmtp)) == 0)
254 		return (0);
255 	if (ret == -1 && errno == EINTR)
256 		return (-1);
257 	return (-2);
258 }
259 
260 void
261 thrd_yield(void)
262 {
263 	thr_yield();
264 }
265 
266 int
267 tss_create(tss_t *key, tss_dtor_t dtor)
268 {
269 	if (pthread_key_create(key, dtor) == 0)
270 		return (thrd_success);
271 	return (thrd_error);
272 }
273 
274 void
275 tss_delete(tss_t key)
276 {
277 	if (pthread_key_delete(key) != 0)
278 		abort();
279 }
280 
281 void *
282 tss_get(tss_t key)
283 {
284 	return (pthread_getspecific(key));
285 }
286 
287 int
288 tss_set(tss_t key, void *val)
289 {
290 	if (pthread_setspecific(key, val) == 0)
291 		return (thrd_success);
292 	return (thrd_error);
293 }
294