Line data Source code
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 11122 : tor_locking_init(void) 30 : { 31 11122 : if (!attr_initialized) { 32 5562 : pthread_mutexattr_init(&attr_recursive); 33 5562 : pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE); 34 5562 : attr_initialized = 1; 35 : } 36 11122 : } 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 5660 : tor_mutex_init(tor_mutex_t *mutex) 42 : { 43 5660 : if (PREDICT_UNLIKELY(!attr_initialized)) 44 : tor_locking_init(); // LCOV_EXCL_LINE 45 5660 : const int err = pthread_mutex_init(&mutex->mutex, &attr_recursive); 46 5660 : if (PREDICT_UNLIKELY(err)) { 47 : // LCOV_EXCL_START 48 : raw_assert_unreached_msg("Error creating a mutex."); 49 : // LCOV_EXCL_STOP 50 : } 51 5660 : } 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 9 : tor_mutex_init_nonrecursive(tor_mutex_t *mutex) 57 : { 58 9 : int err; 59 9 : if (!attr_initialized) 60 : tor_locking_init(); // LCOV_EXCL_LINE 61 9 : err = pthread_mutex_init(&mutex->mutex, NULL); 62 9 : if (PREDICT_UNLIKELY(err)) { 63 : // LCOV_EXCL_START 64 : raw_assert_unreached_msg("Error creating a mutex."); 65 : // LCOV_EXCL_STOP 66 : } 67 9 : } 68 : 69 : /** Wait until <b>m</b> is free, then acquire it. */ 70 : void 71 355082 : tor_mutex_acquire(tor_mutex_t *m) 72 : { 73 355082 : int err; 74 355082 : raw_assert(m); 75 355082 : err = pthread_mutex_lock(&m->mutex); 76 357238 : if (PREDICT_UNLIKELY(err)) { 77 : // LCOV_EXCL_START 78 : raw_assert_unreached_msg("Error locking a mutex."); 79 : // LCOV_EXCL_STOP 80 : } 81 357238 : } 82 : /** Release the lock <b>m</b> so another thread can have it. */ 83 : void 84 356483 : tor_mutex_release(tor_mutex_t *m) 85 : { 86 356483 : int err; 87 356483 : raw_assert(m); 88 356483 : err = pthread_mutex_unlock(&m->mutex); 89 357240 : if (PREDICT_UNLIKELY(err)) { 90 : // LCOV_EXCL_START 91 : raw_assert_unreached_msg("Error unlocking a mutex."); 92 : // LCOV_EXCL_STOP 93 : } 94 357240 : } 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 60 : tor_mutex_uninit(tor_mutex_t *m) 108 : { 109 60 : int err; 110 60 : 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 60 : tor_mutex_acquire(m); 116 60 : tor_mutex_release(m); 117 60 : err = pthread_mutex_destroy(&m->mutex); 118 60 : if (PREDICT_UNLIKELY(err)) { 119 : // LCOV_EXCL_START 120 : raw_assert_unreached_msg("Error destroying a mutex."); 121 : // LCOV_EXCL_STOP 122 : } 123 60 : }