1 /* $OpenBSD: rthread_libc.c,v 1.1 2017/08/15 06:13:24 guenther Exp $ */ 2 3 /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */ 4 5 #include <sys/time.h> 6 7 #include <pthread.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "thread_private.h" /* in libc/include */ 12 13 #include "rthread.h" 14 #include "rthread_cb.h" 15 16 /* 17 * A thread tag is a pointer to a structure of this type. An opaque 18 * tag is used to decouple libc from the thread library. 19 */ 20 struct _thread_tag { 21 pthread_mutex_t m; /* the tag's mutex */ 22 pthread_key_t k; /* a key for private data */ 23 }; 24 25 /* 26 * local mutex to protect against tag creation races. 27 */ 28 static pthread_mutex_t _thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER; 29 30 /* 31 * Initialize a thread tag structure once. This function is called 32 * if the tag is null. Allocation and initialization are controlled 33 * by a mutex. If the tag is not null when the mutex is obtained 34 * the caller lost a race -- some other thread initialized the tag. 35 * This function will never return NULL. 36 */ 37 static void 38 _thread_tag_init(void **tag) 39 { 40 struct _thread_tag *tt; 41 int result; 42 43 result = pthread_mutex_lock(&_thread_tag_mutex); 44 if (result == 0) { 45 if (*tag == NULL) { 46 tt = malloc(sizeof *tt); 47 if (tt != NULL) { 48 result = pthread_mutex_init(&tt->m, NULL); 49 result |= pthread_key_create(&tt->k, free); 50 *tag = tt; 51 } 52 } 53 result |= pthread_mutex_unlock(&_thread_tag_mutex); 54 } 55 if (result != 0) 56 _rthread_debug(1, "tag init failure"); 57 } 58 59 /* 60 * lock the mutex associated with the given tag 61 */ 62 void 63 _thread_tag_lock(void **tag) 64 { 65 struct _thread_tag *tt; 66 67 if (__isthreaded) { 68 if (*tag == NULL) 69 _thread_tag_init(tag); 70 tt = *tag; 71 if (pthread_mutex_lock(&tt->m) != 0) 72 _rthread_debug(1, "tag mutex lock failure"); 73 } 74 } 75 76 /* 77 * unlock the mutex associated with the given tag 78 */ 79 void 80 _thread_tag_unlock(void **tag) 81 { 82 struct _thread_tag *tt; 83 84 if (__isthreaded) { 85 if (*tag == NULL) 86 _thread_tag_init(tag); 87 tt = *tag; 88 if (pthread_mutex_unlock(&tt->m) != 0) 89 _rthread_debug(1, "tag mutex unlock failure"); 90 } 91 } 92 93 /* 94 * return the thread specific data for the given tag. If there 95 * is no data for this thread initialize it from 'storage'. 96 * On any error return 'err'. 97 */ 98 void * 99 _thread_tag_storage(void **tag, void *storage, size_t sz, void *err) 100 { 101 struct _thread_tag *tt; 102 void *ret; 103 104 if (*tag == NULL) 105 _thread_tag_init(tag); 106 tt = *tag; 107 108 ret = pthread_getspecific(tt->k); 109 if (ret == NULL) { 110 ret = malloc(sz); 111 if (ret == NULL) 112 ret = err; 113 else { 114 if (pthread_setspecific(tt->k, ret) == 0) 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 }; 178 179 static pthread_mutex_t malloc_mutex[_MALLOC_MUTEXES] = { 180 &malloc_lock[0], 181 &malloc_lock[1], 182 &malloc_lock[2], 183 &malloc_lock[3] 184 }; 185 186 void 187 _thread_malloc_lock(int i) 188 { 189 pthread_mutex_lock(&malloc_mutex[i]); 190 } 191 192 void 193 _thread_malloc_unlock(int i) 194 { 195 pthread_mutex_unlock(&malloc_mutex[i]); 196 } 197 198 void 199 _thread_malloc_reinit(void) 200 { 201 int i; 202 203 for (i = 0; i < _MALLOC_MUTEXES; i++) { 204 malloc_lock[i].lock = _SPINLOCK_UNLOCKED; 205 #ifndef FUTEX 206 TAILQ_INIT(&malloc_lock[i].lockers); 207 #endif 208 malloc_lock[i].owner = NULL; 209 malloc_lock[i].count = 0; 210 } 211 } 212 213 /* 214 * atexit lock 215 */ 216 static _atomic_lock_t atexit_lock = _SPINLOCK_UNLOCKED; 217 218 void 219 _thread_atexit_lock(void) 220 { 221 _spinlock(&atexit_lock); 222 } 223 224 void 225 _thread_atexit_unlock(void) 226 { 227 _spinunlock(&atexit_lock); 228 } 229 230 /* 231 * atfork lock 232 */ 233 static _atomic_lock_t atfork_lock = _SPINLOCK_UNLOCKED; 234 235 void 236 _thread_atfork_lock(void) 237 { 238 _spinlock(&atfork_lock); 239 } 240 241 void 242 _thread_atfork_unlock(void) 243 { 244 _spinunlock(&atfork_lock); 245 } 246 247 /* 248 * arc4random lock 249 */ 250 static _atomic_lock_t arc4_lock = _SPINLOCK_UNLOCKED; 251 252 void 253 _thread_arc4_lock(void) 254 { 255 _spinlock(&arc4_lock); 256 } 257 258 void 259 _thread_arc4_unlock(void) 260 { 261 _spinunlock(&arc4_lock); 262 } 263