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, 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 209 thrd_current(void) 210 { 211 return (pthread_self()); 212 } 213 214 int 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 223 thrd_equal(thrd_t t1, thrd_t t2) 224 { 225 return (pthread_equal(t1, t2)); 226 } 227 228 _NORETURN_KYWD void 229 thrd_exit(int res) 230 { 231 pthread_exit((void *)(uintptr_t)res); 232 } 233 234 int 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 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 262 thrd_yield(void) 263 { 264 thr_yield(); 265 } 266 267 int 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 276 tss_delete(tss_t key) 277 { 278 if (pthread_key_delete(key) != 0) 279 abort(); 280 } 281 282 void * 283 tss_get(tss_t key) 284 { 285 return (pthread_getspecific(key)); 286 } 287 288 int 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