1 /* $OpenBSD: rthread_libc.c,v 1.2 2017/09/05 02:40:54 guenther 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) 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, free); 46 *tag = tt; 47 } 48 } 49 result |= pthread_mutex_unlock(&_thread_tag_mutex); 50 } 51 if (result != 0) 52 _rthread_debug(1, "tag init failure"); 53 } 54 55 /* 56 * lock the mutex associated with the given tag 57 */ 58 void 59 _thread_tag_lock(void **tag) 60 { 61 struct _thread_tag *tt; 62 63 if (__isthreaded) { 64 if (*tag == NULL) 65 _thread_tag_init(tag); 66 tt = *tag; 67 if (pthread_mutex_lock(&tt->m) != 0) 68 _rthread_debug(1, "tag mutex lock failure"); 69 } 70 } 71 72 /* 73 * unlock the mutex associated with the given tag 74 */ 75 void 76 _thread_tag_unlock(void **tag) 77 { 78 struct _thread_tag *tt; 79 80 if (__isthreaded) { 81 if (*tag == NULL) 82 _thread_tag_init(tag); 83 tt = *tag; 84 if (pthread_mutex_unlock(&tt->m) != 0) 85 _rthread_debug(1, "tag mutex unlock failure"); 86 } 87 } 88 89 /* 90 * return the thread specific data for the given tag. If there 91 * is no data for this thread initialize it from 'storage'. 92 * On any error return 'err'. 93 */ 94 void * 95 _thread_tag_storage(void **tag, void *storage, size_t sz, void *err) 96 { 97 struct _thread_tag *tt; 98 void *ret; 99 100 if (*tag == NULL) 101 _thread_tag_init(tag); 102 tt = *tag; 103 104 ret = pthread_getspecific(tt->k); 105 if (ret == NULL) { 106 ret = malloc(sz); 107 if (ret == NULL) 108 ret = err; 109 else { 110 if (pthread_setspecific(tt->k, ret) == 0) 111 memcpy(ret, storage, sz); 112 else { 113 free(ret); 114 ret = err; 115 } 116 } 117 } 118 return ret; 119 } 120 121 void 122 _thread_mutex_lock(void **mutex) 123 { 124 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 125 126 if (pthread_mutex_lock(pmutex) != 0) 127 _rthread_debug(1, "mutex lock failure"); 128 } 129 130 void 131 _thread_mutex_unlock(void **mutex) 132 { 133 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 134 135 if (pthread_mutex_unlock(pmutex) != 0) 136 _rthread_debug(1, "mutex unlock failure"); 137 } 138 139 void 140 _thread_mutex_destroy(void **mutex) 141 { 142 pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; 143 144 if (pthread_mutex_destroy(pmutex) != 0) 145 _rthread_debug(1, "mutex destroy failure"); 146 } 147 148 /* 149 * the malloc lock 150 */ 151 #ifndef FUTEX 152 #define MALLOC_LOCK_INITIALIZER(n) { \ 153 _SPINLOCK_UNLOCKED, \ 154 TAILQ_HEAD_INITIALIZER(malloc_lock[n].lockers), \ 155 PTHREAD_MUTEX_DEFAULT, \ 156 NULL, \ 157 0, \ 158 -1 } 159 #else 160 #define MALLOC_LOCK_INITIALIZER(n) { \ 161 _SPINLOCK_UNLOCKED, \ 162 PTHREAD_MUTEX_DEFAULT, \ 163 NULL, \ 164 0, \ 165 -1 } 166 #endif 167 168 static struct pthread_mutex malloc_lock[_MALLOC_MUTEXES] = { 169 MALLOC_LOCK_INITIALIZER(0), 170 MALLOC_LOCK_INITIALIZER(1), 171 MALLOC_LOCK_INITIALIZER(2), 172 MALLOC_LOCK_INITIALIZER(3) 173 }; 174 175 static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = { 176 &malloc_lock[0], 177 &malloc_lock[1], 178 &malloc_lock[2], 179 &malloc_lock[3] 180 }; 181 182 void 183 _thread_malloc_lock(int i) 184 { 185 pthread_mutex_lock(&malloc_mutex[i]); 186 } 187 188 void 189 _thread_malloc_unlock(int i) 190 { 191 pthread_mutex_unlock(&malloc_mutex[i]); 192 } 193 194 static void 195 _thread_malloc_reinit(void) 196 { 197 int i; 198 199 for (i = 0; i < _MALLOC_MUTEXES; i++) { 200 malloc_lock[i].lock = _SPINLOCK_UNLOCKED; 201 #ifndef FUTEX 202 TAILQ_INIT(&malloc_lock[i].lockers); 203 #endif 204 malloc_lock[i].owner = NULL; 205 malloc_lock[i].count = 0; 206 } 207 } 208 209 /* 210 * atexit lock 211 */ 212 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED; 213 214 void 215 _thread_atexit_lock(void) 216 { 217 _spinlock(&atexit_lock); 218 } 219 220 void 221 _thread_atexit_unlock(void) 222 { 223 _spinunlock(&atexit_lock); 224 } 225 226 /* 227 * atfork lock 228 */ 229 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED; 230 231 void 232 _thread_atfork_lock(void) 233 { 234 _spinlock(&atfork_lock); 235 } 236 237 void 238 _thread_atfork_unlock(void) 239 { 240 _spinunlock(&atfork_lock); 241 } 242 243 /* 244 * arc4random lock 245 */ 246 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED; 247 248 void 249 _thread_arc4_lock(void) 250 { 251 _spinlock(&arc4_lock); 252 } 253 254 void 255 _thread_arc4_unlock(void) 256 { 257 _spinunlock(&arc4_lock); 258 } 259 260 pid_t 261 _thread_dofork(pid_t (*sys_fork)(void)) 262 { 263 int i; 264 pid_t newid; 265 266 _thread_atexit_lock(); 267 for (i = 0; i < _MALLOC_MUTEXES; i++) 268 _thread_malloc_lock(i); 269 _thread_arc4_lock(); 270 271 newid = sys_fork(); 272 273 _thread_arc4_unlock(); 274 if (newid == 0) 275 _thread_malloc_reinit(); 276 else 277 for (i = 0; i < _MALLOC_MUTEXES; i++) 278 _thread_malloc_unlock(i); 279 _thread_atexit_unlock(); 280 281 return newid; 282 } 283 284