compat_mutex_pthreads.c (3635B)
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_mutex_pthreads.c 8 * 9 * \brief Implement the tor_mutex API using pthread_mutex_t. 10 **/ 11 12 #include "lib/lock/compat_mutex.h" 13 #include "lib/cc/compat_compiler.h" 14 #include "lib/err/torerr.h" 15 16 /** A mutex attribute that we're going to use to tell pthreads that we want 17 * "recursive" mutexes (i.e., once we can re-lock if we're already holding 18 * them.) */ 19 static pthread_mutexattr_t attr_recursive; 20 /** 21 * True iff <b>attr_recursive</b> has been initialized. 22 **/ 23 static int attr_initialized = 0; 24 25 /** 26 * Initialize the locking module, if it is not already initialized. 27 **/ 28 void 29 tor_locking_init(void) 30 { 31 if (!attr_initialized) { 32 pthread_mutexattr_init(&attr_recursive); 33 pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE); 34 attr_initialized = 1; 35 } 36 } 37 38 /** Initialize <b>mutex</b> so it can be locked. Every mutex must be set 39 * up with tor_mutex_init() or tor_mutex_new(); not both. */ 40 void 41 tor_mutex_init(tor_mutex_t *mutex) 42 { 43 if (PREDICT_UNLIKELY(!attr_initialized)) 44 tor_locking_init(); // LCOV_EXCL_LINE 45 const int err = pthread_mutex_init(&mutex->mutex, &attr_recursive); 46 if (PREDICT_UNLIKELY(err)) { 47 // LCOV_EXCL_START 48 raw_assert_unreached_msg("Error creating a mutex."); 49 // LCOV_EXCL_STOP 50 } 51 } 52 53 /** As tor_mutex_init, but initialize a mutex suitable that may be 54 * non-recursive, if the OS supports that. */ 55 void 56 tor_mutex_init_nonrecursive(tor_mutex_t *mutex) 57 { 58 int err; 59 if (!attr_initialized) 60 tor_locking_init(); // LCOV_EXCL_LINE 61 err = pthread_mutex_init(&mutex->mutex, NULL); 62 if (PREDICT_UNLIKELY(err)) { 63 // LCOV_EXCL_START 64 raw_assert_unreached_msg("Error creating a mutex."); 65 // LCOV_EXCL_STOP 66 } 67 } 68 69 /** Wait until <b>m</b> is free, then acquire it. */ 70 void 71 tor_mutex_acquire(tor_mutex_t *m) 72 { 73 int err; 74 raw_assert(m); 75 err = pthread_mutex_lock(&m->mutex); 76 if (PREDICT_UNLIKELY(err)) { 77 // LCOV_EXCL_START 78 raw_assert_unreached_msg("Error locking a mutex."); 79 // LCOV_EXCL_STOP 80 } 81 } 82 /** Release the lock <b>m</b> so another thread can have it. */ 83 void 84 tor_mutex_release(tor_mutex_t *m) 85 { 86 int err; 87 raw_assert(m); 88 err = pthread_mutex_unlock(&m->mutex); 89 if (PREDICT_UNLIKELY(err)) { 90 // LCOV_EXCL_START 91 raw_assert_unreached_msg("Error unlocking a mutex."); 92 // LCOV_EXCL_STOP 93 } 94 } 95 /** Clean up the mutex <b>m</b> so that it no longer uses any system 96 * resources. Does not free <b>m</b>. This function must only be called on 97 * mutexes from tor_mutex_init(). 98 * 99 * Destroying a locked mutex is undefined behaviour. Global mutexes may be 100 * locked when they are passed to this function, because multiple threads can 101 * still access them. So we can either: 102 * - destroy on shutdown, and re-initialise when tor re-initialises, or 103 * - skip destroying and re-initialisation, using a sentinel variable. 104 * See #31735 for details. 105 */ 106 void 107 tor_mutex_uninit(tor_mutex_t *m) 108 { 109 int err; 110 raw_assert(m); 111 /* If the mutex is already locked, wait until after it is unlocked to destroy 112 * it. Locking and releasing the mutex makes undefined behaviour less likely, 113 * but does not prevent it. Another thread can lock the mutex between release 114 * and destroy. */ 115 tor_mutex_acquire(m); 116 tor_mutex_release(m); 117 err = pthread_mutex_destroy(&m->mutex); 118 if (PREDICT_UNLIKELY(err)) { 119 // LCOV_EXCL_START 120 raw_assert_unreached_msg("Error destroying a mutex."); 121 // LCOV_EXCL_STOP 122 } 123 }