1/* $OpenBSD: rthread_libc.c,v 1.4 2021/01/06 19:54:17 otto Exp $ */ 2 3/* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */ 4 5#include <pthread.h> 6#include <stdlib.h> 7#include <string.h> 8 9#include "rthread.h" 10#include "rthread_cb.h" 11 12/* 13 * A thread tag is a pointer to a structure of this type. An opaque 14 * tag is used to decouple libc from the thread library. 15 */ 16struct _thread_tag { 17 pthread_mutex_t m; /* the tag's mutex */ 18 pthread_key_t k; /* a key for private data */ 19}; 20 21/* 22 * local mutex to protect against tag creation races. 23 */ 24static pthread_mutex_t _thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER; 25 26/* 27 * Initialize a thread tag structure once. This function is called 28 * if the tag is null. Allocation and initialization are controlled 29 * by a mutex. If the tag is not null when the mutex is obtained 30 * the caller lost a race -- some other thread initialized the tag. 31 * This function will never return NULL. 32 */ 33static void 34_thread_tag_init(void **tag, void (*dt)(void *)) 35{ 36 struct _thread_tag *tt; 37 int result; 38 39 result = pthread_mutex_lock(&_thread_tag_mutex); 40 if (result == 0) { 41 if (*tag == NULL) { 42 tt = malloc(sizeof *tt); 43 if (tt != NULL) { 44 result = pthread_mutex_init(&tt->m, NULL); 45 result |= pthread_key_create(&tt->k, dt ? dt : 46 free); 47 *tag = tt; 48 } 49 } 50 result |= pthread_mutex_unlock(&_thread_tag_mutex); 51 } 52 if (result != 0) 53 _rthread_debug(1, "tag init failure"); 54} 55 56/* 57 * lock the mutex associated with the given tag 58 */ 59void 60_thread_tag_lock(void **tag) 61{ 62 struct _thread_tag *tt; 63 64 if (__isthreaded) { 65 if (*tag == NULL) 66 _thread_tag_init(tag, NULL); 67 tt = *tag; 68 if (pthread_mutex_lock(&tt->m) != 0) 69 _rthread_debug(1, "tag mutex lock failure"); 70 } 71} 72 73/* 74 * unlock the mutex associated with the given tag 75 */ 76void 77_thread_tag_unlock(void **tag) 78{ 79 struct _thread_tag *tt; 80 81 if (__isthreaded) { 82 if (*tag == NULL) 83 _thread_tag_init(tag, NULL); 84 tt = *tag; 85 if (pthread_mutex_unlock(&tt->m) != 0) 86 _rthread_debug(1, "tag mutex unlock failure"); 87 } 88} 89 90/* 91 * return the thread specific data for the given tag. If there 92 * is no data for this thread allocate and initialize it from 'storage' 93 * or clear it for non-main threads. 94 * On any error return 'err'. 95 */ 96void * 97_thread_tag_storage(void **tag, void *storage, size_t sz, void (*dt)(void *), 98 void *err) 99{ 100 struct _thread_tag *tt; 101 void *ret; 102 103 if (*tag == NULL) 104 _thread_tag_init(tag, dt); 105 tt = *tag; 106 107 ret = pthread_getspecific(tt->k); 108 if (ret == NULL) { 109 ret = calloc(1, sz); 110 if (ret == NULL) 111 ret = err; 112 else { 113 if (pthread_setspecific(tt->k, ret) == 0) { 114 if (pthread_self() == &_initial_thread) 115 memcpy(ret, storage, sz); 116 } else { 117 free(ret); 118 ret = err; 119 } 120 } 121 } 122 return ret; 123} 124 125void 126_thread_mutex_lock(void **mutex) 127{ 128 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 129 130 if (pthread_mutex_lock(pmutex) != 0) 131 _rthread_debug(1, "mutex lock failure"); 132} 133 134void 135_thread_mutex_unlock(void **mutex) 136{ 137 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 138 139 if (pthread_mutex_unlock(pmutex) != 0) 140 _rthread_debug(1, "mutex unlock failure"); 141} 142 143void 144_thread_mutex_destroy(void **mutex) 145{ 146 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 147 148 if (pthread_mutex_destroy(pmutex) != 0) 149 _rthread_debug(1, "mutex destroy failure"); 150} 151 152/* 153 * the malloc lock 154 */ 155#ifndef FUTEX 156#define MALLOC_LOCK_INITIALIZER(n) { \ 157 _SPINLOCK_UNLOCKED, \ 158 TAILQ_HEAD_INITIALIZER(malloc_lock[n].lockers), \ 159 PTHREAD_MUTEX_DEFAULT, \ 160 NULL, \ 161 0, \ 162 -1 } 163#else 164#define MALLOC_LOCK_INITIALIZER(n) { \ 165 _SPINLOCK_UNLOCKED, \ 166 PTHREAD_MUTEX_DEFAULT, \ 167 NULL, \ 168 0, \ 169 -1 } 170#endif 171 172static struct pthread_mutex malloc_lock[_MALLOC_MUTEXES] = { 173 MALLOC_LOCK_INITIALIZER(0), 174 MALLOC_LOCK_INITIALIZER(1), 175 MALLOC_LOCK_INITIALIZER(2), 176 MALLOC_LOCK_INITIALIZER(3), 177 MALLOC_LOCK_INITIALIZER(4), 178 MALLOC_LOCK_INITIALIZER(5), 179 MALLOC_LOCK_INITIALIZER(6), 180 MALLOC_LOCK_INITIALIZER(7), 181 MALLOC_LOCK_INITIALIZER(8), 182 MALLOC_LOCK_INITIALIZER(9), 183 MALLOC_LOCK_INITIALIZER(10), 184 MALLOC_LOCK_INITIALIZER(11), 185 MALLOC_LOCK_INITIALIZER(12), 186 MALLOC_LOCK_INITIALIZER(13), 187 MALLOC_LOCK_INITIALIZER(14), 188 MALLOC_LOCK_INITIALIZER(15), 189 MALLOC_LOCK_INITIALIZER(16), 190 MALLOC_LOCK_INITIALIZER(17), 191 MALLOC_LOCK_INITIALIZER(18), 192 MALLOC_LOCK_INITIALIZER(19), 193 MALLOC_LOCK_INITIALIZER(20), 194 MALLOC_LOCK_INITIALIZER(21), 195 MALLOC_LOCK_INITIALIZER(22), 196 MALLOC_LOCK_INITIALIZER(23), 197 MALLOC_LOCK_INITIALIZER(24), 198 MALLOC_LOCK_INITIALIZER(25), 199 MALLOC_LOCK_INITIALIZER(26), 200 MALLOC_LOCK_INITIALIZER(27), 201 MALLOC_LOCK_INITIALIZER(28), 202 MALLOC_LOCK_INITIALIZER(29), 203 MALLOC_LOCK_INITIALIZER(30), 204 MALLOC_LOCK_INITIALIZER(31) 205}; 206 207static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = { 208 &malloc_lock[0], 209 &malloc_lock[1], 210 &malloc_lock[2], 211 &malloc_lock[3], 212 &malloc_lock[4], 213 &malloc_lock[5], 214 &malloc_lock[6], 215 &malloc_lock[7], 216 &malloc_lock[8], 217 &malloc_lock[9], 218 &malloc_lock[10], 219 &malloc_lock[11], 220 &malloc_lock[12], 221 &malloc_lock[13], 222 &malloc_lock[14], 223 &malloc_lock[15], 224 &malloc_lock[16], 225 &malloc_lock[17], 226 &malloc_lock[18], 227 &malloc_lock[19], 228 &malloc_lock[20], 229 &malloc_lock[21], 230 &malloc_lock[22], 231 &malloc_lock[23], 232 &malloc_lock[24], 233 &malloc_lock[25], 234 &malloc_lock[26], 235 &malloc_lock[27], 236 &malloc_lock[28], 237 &malloc_lock[29], 238 &malloc_lock[30], 239 &malloc_lock[31] 240}; 241 242void 243_thread_malloc_lock(int i) 244{ 245 pthread_mutex_lock(&malloc_mutex[i]); 246} 247 248void 249_thread_malloc_unlock(int i) 250{ 251 pthread_mutex_unlock(&malloc_mutex[i]); 252} 253 254static void 255_thread_malloc_reinit(void) 256{ 257 int i; 258 259 for (i = 0; i < _MALLOC_MUTEXES; i++) { 260 malloc_lock[i].lock = _SPINLOCK_UNLOCKED; 261#ifndef FUTEX 262 TAILQ_INIT(&malloc_lock[i].lockers); 263#endif 264 malloc_lock[i].owner = NULL; 265 malloc_lock[i].count = 0; 266 } 267} 268 269/* 270 * atexit lock 271 */ 272static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED; 273 274void 275_thread_atexit_lock(void) 276{ 277 _spinlock(&atexit_lock); 278} 279 280void 281_thread_atexit_unlock(void) 282{ 283 _spinunlock(&atexit_lock); 284} 285 286/* 287 * atfork lock 288 */ 289static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED; 290 291void 292_thread_atfork_lock(void) 293{ 294 _spinlock(&atfork_lock); 295} 296 297void 298_thread_atfork_unlock(void) 299{ 300 _spinunlock(&atfork_lock); 301} 302 303/* 304 * arc4random lock 305 */ 306static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED; 307 308void 309_thread_arc4_lock(void) 310{ 311 _spinlock(&arc4_lock); 312} 313 314void 315_thread_arc4_unlock(void) 316{ 317 _spinunlock(&arc4_lock); 318} 319 320pid_t 321_thread_dofork(pid_t (*sys_fork)(void)) 322{ 323 int i; 324 pid_t newid; 325 326 _thread_atexit_lock(); 327 for (i = 0; i < _MALLOC_MUTEXES; i++) 328 _thread_malloc_lock(i); 329 _thread_arc4_lock(); 330 331 newid = sys_fork(); 332 333 _thread_arc4_unlock(); 334 if (newid == 0) 335 _thread_malloc_reinit(); 336 else 337 for (i = 0; i < _MALLOC_MUTEXES; i++) 338 _thread_malloc_unlock(i); 339 _thread_atexit_unlock(); 340 341 return newid; 342} 343 344