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 */ 16 struct _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 */ 24 static 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 */ 33 static 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 */ 59 void 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 */ 76 void 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 */ 96 void * 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 125 void 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 134 void 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 143 void 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 172 static 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 207 static 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 242 void 243 _thread_malloc_lock(int i) 244 { 245 pthread_mutex_lock(&malloc_mutex[i]); 246 } 247 248 void 249 _thread_malloc_unlock(int i) 250 { 251 pthread_mutex_unlock(&malloc_mutex[i]); 252 } 253 254 static 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 */ 272 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED; 273 274 void 275 _thread_atexit_lock(void) 276 { 277 _spinlock(&atexit_lock); 278 } 279 280 void 281 _thread_atexit_unlock(void) 282 { 283 _spinunlock(&atexit_lock); 284 } 285 286 /* 287 * atfork lock 288 */ 289 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED; 290 291 void 292 _thread_atfork_lock(void) 293 { 294 _spinlock(&atfork_lock); 295 } 296 297 void 298 _thread_atfork_unlock(void) 299 { 300 _spinunlock(&atfork_lock); 301 } 302 303 /* 304 * arc4random lock 305 */ 306 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED; 307 308 void 309 _thread_arc4_lock(void) 310 { 311 _spinlock(&arc4_lock); 312 } 313 314 void 315 _thread_arc4_unlock(void) 316 { 317 _spinunlock(&arc4_lock); 318 } 319 320 pid_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