compat_pthreads.c (7232B)
1 /* Copyright (c) 2003-2004, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file compat_pthreads.c 8 * 9 * \brief Implementation for the pthreads-based multithreading backend 10 * functions. 11 */ 12 13 #include "orconfig.h" 14 #include "lib/thread/threads.h" 15 #include "lib/wallclock/timeval.h" 16 #include "lib/log/log.h" 17 #include "lib/log/util_bug.h" 18 19 #include <sys/time.h> 20 #include <pthread.h> 21 #include <signal.h> 22 #include <time.h> 23 #include <errno.h> 24 #include <string.h> 25 26 /** Wraps a void (*)(void*) function and its argument so we can 27 * invoke them in a way pthreads would expect. 28 */ 29 typedef struct tor_pthread_data_t { 30 void (*func)(void *); 31 void *data; 32 } tor_pthread_data_t; 33 /** Given a tor_pthread_data_t <b>_data</b>, call _data->func(d->data) 34 * and free _data. Used to make sure we can call functions the way pthread 35 * expects. */ 36 static void * 37 tor_pthread_helper_fn(void *_data) 38 { 39 tor_pthread_data_t *data = _data; 40 void (*func)(void*); 41 void *arg; 42 /* mask signals to worker threads to avoid SIGPIPE, etc */ 43 sigset_t sigs; 44 /* We're in a subthread; don't handle any signals here. */ 45 sigfillset(&sigs); 46 pthread_sigmask(SIG_SETMASK, &sigs, NULL); 47 48 func = data->func; 49 arg = data->data; 50 tor_free(_data); 51 func(arg); 52 return NULL; 53 } 54 /** 55 * A pthread attribute to make threads start detached. 56 */ 57 static pthread_attr_t attr_detached; 58 /** True iff we've called tor_threads_init() */ 59 static int threads_initialized = 0; 60 61 /** Minimalist interface to run a void function in the background. On 62 * Unix calls pthread_create, on win32 calls beginthread. Returns -1 on 63 * failure. 64 * func should not return, but rather should call spawn_exit. 65 * 66 * NOTE: if <b>data</b> is used, it should not be allocated on the stack, 67 * since in a multithreaded environment, there is no way to be sure that 68 * the caller's stack will still be around when the called function is 69 * running. 70 */ 71 int 72 spawn_func(void (*func)(void *), void *data) 73 { 74 pthread_t thread; 75 tor_pthread_data_t *d; 76 if (PREDICT_UNLIKELY(!threads_initialized)) { 77 tor_threads_init(); 78 } 79 d = tor_malloc(sizeof(tor_pthread_data_t)); 80 d->data = data; 81 d->func = func; 82 if (pthread_create(&thread, &attr_detached, tor_pthread_helper_fn, d)) { 83 tor_free(d); 84 return -1; 85 } 86 87 return 0; 88 } 89 90 /** End the current thread/process. 91 */ 92 void 93 spawn_exit(void) 94 { 95 pthread_exit(NULL); 96 } 97 98 /** Return an integer representing this thread. */ 99 unsigned long 100 tor_get_thread_id(void) 101 { 102 union { 103 pthread_t thr; 104 unsigned long id; 105 } r; 106 r.thr = pthread_self(); 107 return r.id; 108 } 109 110 /* Conditions. */ 111 112 /** Initialize an already-allocated condition variable. */ 113 int 114 tor_cond_init(tor_cond_t *cond) 115 { 116 pthread_condattr_t condattr; 117 118 memset(cond, 0, sizeof(tor_cond_t)); 119 /* Default condition attribute. Might be used if clock monotonic is 120 * available else this won't affect anything. */ 121 if (pthread_condattr_init(&condattr)) { 122 return -1; 123 } 124 125 #if defined(HAVE_CLOCK_GETTIME) 126 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \ 127 defined(CLOCK_MONOTONIC) 128 /* Use monotonic time so when we timedwait() on it, any clock adjustment 129 * won't affect the timeout value. */ 130 if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC)) { 131 return -1; 132 } 133 #define USE_COND_CLOCK CLOCK_MONOTONIC 134 #else /* !(defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && ...) */ 135 /* On OSX Sierra, there is no pthread_condattr_setclock, so we are stuck 136 * with the realtime clock. 137 */ 138 #define USE_COND_CLOCK CLOCK_REALTIME 139 #endif /* defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && ... */ 140 #endif /* defined(HAVE_CLOCK_GETTIME) */ 141 if (pthread_cond_init(&cond->cond, &condattr)) { 142 return -1; 143 } 144 return 0; 145 } 146 147 /** Release all resources held by <b>cond</b>, but do not free <b>cond</b> 148 * itself. */ 149 void 150 tor_cond_uninit(tor_cond_t *cond) 151 { 152 if (pthread_cond_destroy(&cond->cond)) { 153 // LCOV_EXCL_START 154 log_warn(LD_GENERAL,"Error freeing condition: %s", strerror(errno)); 155 return; 156 // LCOV_EXCL_STOP 157 } 158 } 159 /** Wait until one of the tor_cond_signal functions is called on <b>cond</b>. 160 * (If <b>tv</b> is set, and that amount of time passes with no signal to 161 * <b>cond</b>, return anyway. All waiters on the condition must wait holding 162 * the same <b>mutex</b>. All signallers should hold that mutex. The mutex 163 * needs to have been allocated with tor_mutex_init_for_cond(). 164 * 165 * Returns 0 on success, -1 on failure, 1 on timeout. */ 166 int 167 tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, const struct timeval *tv) 168 { 169 int r; 170 if (tv == NULL) { 171 while (1) { 172 r = pthread_cond_wait(&cond->cond, &mutex->mutex); 173 if (r == EINTR) { 174 /* EINTR should be impossible according to POSIX, but POSIX, like the 175 * Pirate's Code, is apparently treated "more like what you'd call 176 * guidelines than actual rules." */ 177 continue; // LCOV_EXCL_LINE 178 } 179 return r ? -1 : 0; 180 } 181 } else { 182 struct timeval tvnow, tvsum; 183 struct timespec ts; 184 while (1) { 185 #if defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK) 186 if (clock_gettime(USE_COND_CLOCK, &ts) < 0) { 187 return -1; 188 } 189 tvnow.tv_sec = ts.tv_sec; 190 tvnow.tv_usec = (int)(ts.tv_nsec / 1000); 191 timeradd(tv, &tvnow, &tvsum); 192 #else /* !(defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK)) */ 193 if (gettimeofday(&tvnow, NULL) < 0) 194 return -1; 195 timeradd(tv, &tvnow, &tvsum); 196 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK) */ 197 198 ts.tv_sec = tvsum.tv_sec; 199 ts.tv_nsec = tvsum.tv_usec * 1000; 200 201 r = pthread_cond_timedwait(&cond->cond, &mutex->mutex, &ts); 202 if (r == 0) 203 return 0; 204 else if (r == ETIMEDOUT) 205 return 1; 206 else if (r == EINTR) 207 continue; 208 else 209 return -1; 210 } 211 } 212 } 213 /** Wake up one of the waiters on <b>cond</b>. */ 214 void 215 tor_cond_signal_one(tor_cond_t *cond) 216 { 217 pthread_cond_signal(&cond->cond); 218 } 219 /** Wake up all of the waiters on <b>cond</b>. */ 220 void 221 tor_cond_signal_all(tor_cond_t *cond) 222 { 223 pthread_cond_broadcast(&cond->cond); 224 } 225 226 int 227 tor_threadlocal_init(tor_threadlocal_t *threadlocal) 228 { 229 int err = pthread_key_create(&threadlocal->key, NULL); 230 return err ? -1 : 0; 231 } 232 233 void 234 tor_threadlocal_destroy(tor_threadlocal_t *threadlocal) 235 { 236 pthread_key_delete(threadlocal->key); 237 memset(threadlocal, 0, sizeof(tor_threadlocal_t)); 238 } 239 240 void * 241 tor_threadlocal_get(tor_threadlocal_t *threadlocal) 242 { 243 return pthread_getspecific(threadlocal->key); 244 } 245 246 void 247 tor_threadlocal_set(tor_threadlocal_t *threadlocal, void *value) 248 { 249 int err = pthread_setspecific(threadlocal->key, value); 250 tor_assert(err == 0); 251 } 252 253 /** Set up common structures for use by threading. */ 254 void 255 tor_threads_init(void) 256 { 257 if (!threads_initialized) { 258 tor_locking_init(); 259 const int ret1 = pthread_attr_init(&attr_detached); 260 tor_assert(ret1 == 0); 261 #ifndef PTHREAD_CREATE_DETACHED 262 #define PTHREAD_CREATE_DETACHED 1 263 #endif 264 const int ret2 = 265 pthread_attr_setdetachstate(&attr_detached, PTHREAD_CREATE_DETACHED); 266 tor_assert(ret2 == 0); 267 threads_initialized = 1; 268 } 269 set_main_thread(); 270 }