tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_threads.c (8370B)


      1 /* Copyright (c) 2001-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 #include "orconfig.h"
      7 #include "core/or/or.h"
      8 #include "lib/thread/threads.h"
      9 #include "test/test.h"
     10 
     11 /** mutex for thread test to stop the threads hitting data at the same time. */
     12 static tor_mutex_t *thread_test_mutex_ = NULL;
     13 /** mutexes for the thread test to make sure that the threads have to
     14 * interleave somewhat. */
     15 static tor_mutex_t *thread_test_start1_ = NULL,
     16                   *thread_test_start2_ = NULL;
     17 /** Shared strmap for the thread test. */
     18 static strmap_t *thread_test_strmap_ = NULL;
     19 /** The name of thread1 for the thread test */
     20 static char *thread1_name_ = NULL;
     21 /** The name of thread2 for the thread test */
     22 static char *thread2_name_ = NULL;
     23 
     24 static int thread_fns_failed = 0;
     25 
     26 static unsigned long thread_fn_tid1, thread_fn_tid2;
     27 
     28 static void thread_test_func_(void* _s) ATTR_NORETURN;
     29 
     30 /** How many iterations have the threads in the unit test run? */
     31 static tor_threadlocal_t count;
     32 
     33 /** Helper function for threading unit tests: This function runs in a
     34 * subthread. It grabs its own mutex (start1 or start2) to make sure that it
     35 * should start, then it repeatedly alters _test_thread_strmap protected by
     36 * thread_test_mutex_. */
     37 static void
     38 thread_test_func_(void* _s)
     39 {
     40  char *s = _s;
     41  int i;
     42  tor_mutex_t *m;
     43  char buf[64];
     44  char **cp;
     45  int *mycount = tor_malloc_zero(sizeof(int));
     46  tor_threadlocal_set(&count, mycount);
     47  if (!strcmp(s, "thread 1")) {
     48    m = thread_test_start1_;
     49    cp = &thread1_name_;
     50    thread_fn_tid1 = tor_get_thread_id();
     51  } else {
     52    m = thread_test_start2_;
     53    cp = &thread2_name_;
     54    thread_fn_tid2 = tor_get_thread_id();
     55  }
     56 
     57  tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
     58  *cp = tor_strdup(buf);
     59 
     60  tor_mutex_acquire(m);
     61 
     62  for (i=0; i<10000; ++i) {
     63    tor_mutex_acquire(thread_test_mutex_);
     64    strmap_set(thread_test_strmap_, "last to run", *cp);
     65    tor_mutex_release(thread_test_mutex_);
     66    int *tls_count = tor_threadlocal_get(&count);
     67    tor_assert(tls_count == mycount);
     68    ++*tls_count;
     69  }
     70  tor_mutex_acquire(thread_test_mutex_);
     71  strmap_set(thread_test_strmap_, s, *cp);
     72  if (in_main_thread())
     73    ++thread_fns_failed;
     74  tor_mutex_release(thread_test_mutex_);
     75 
     76  tor_free(mycount);
     77 
     78  tor_mutex_release(m);
     79 
     80  spawn_exit();
     81 }
     82 
     83 /** Run unit tests for threading logic. */
     84 static void
     85 test_threads_basic(void *arg)
     86 {
     87  char *s1 = NULL, *s2 = NULL;
     88  int done = 0, timedout = 0;
     89  time_t started;
     90  (void) arg;
     91  tt_int_op(tor_threadlocal_init(&count), OP_EQ, 0);
     92 
     93  set_main_thread();
     94 
     95  thread_test_mutex_ = tor_mutex_new();
     96  thread_test_start1_ = tor_mutex_new();
     97  thread_test_start2_ = tor_mutex_new();
     98  thread_test_strmap_ = strmap_new();
     99  s1 = tor_strdup("thread 1");
    100  s2 = tor_strdup("thread 2");
    101  tor_mutex_acquire(thread_test_start1_);
    102  tor_mutex_acquire(thread_test_start2_);
    103  spawn_func(thread_test_func_, s1);
    104  spawn_func(thread_test_func_, s2);
    105  tor_mutex_release(thread_test_start2_);
    106  tor_mutex_release(thread_test_start1_);
    107  started = time(NULL);
    108  while (!done) {
    109    tor_mutex_acquire(thread_test_mutex_);
    110    strmap_assert_ok(thread_test_strmap_);
    111    if (strmap_get(thread_test_strmap_, "thread 1") &&
    112        strmap_get(thread_test_strmap_, "thread 2")) {
    113      done = 1;
    114    } else if (time(NULL) > started + 150) {
    115      timedout = done = 1;
    116    }
    117    tor_mutex_release(thread_test_mutex_);
    118    /* Prevent the main thread from starving the worker threads. */
    119    tor_sleep_msec(10);
    120  }
    121  tor_mutex_acquire(thread_test_start1_);
    122  tor_mutex_release(thread_test_start1_);
    123  tor_mutex_acquire(thread_test_start2_);
    124  tor_mutex_release(thread_test_start2_);
    125 
    126  tor_mutex_free(thread_test_mutex_);
    127 
    128  if (timedout) {
    129    tt_assert(strmap_get(thread_test_strmap_, "thread 1"));
    130    tt_assert(strmap_get(thread_test_strmap_, "thread 2"));
    131    tt_assert(!timedout);
    132  }
    133 
    134  /* different thread IDs. */
    135  tt_assert(strcmp(strmap_get(thread_test_strmap_, "thread 1"),
    136                     strmap_get(thread_test_strmap_, "thread 2")));
    137  tt_assert(!strcmp(strmap_get(thread_test_strmap_, "thread 1"),
    138                      strmap_get(thread_test_strmap_, "last to run")) ||
    139              !strcmp(strmap_get(thread_test_strmap_, "thread 2"),
    140                      strmap_get(thread_test_strmap_, "last to run")));
    141 
    142  tt_int_op(thread_fns_failed, OP_EQ, 0);
    143  tt_int_op(thread_fn_tid1, OP_NE, thread_fn_tid2);
    144 
    145 done:
    146  tor_free(s1);
    147  tor_free(s2);
    148  tor_free(thread1_name_);
    149  tor_free(thread2_name_);
    150  if (thread_test_strmap_)
    151    strmap_free(thread_test_strmap_, NULL);
    152  if (thread_test_start1_)
    153    tor_mutex_free(thread_test_start1_);
    154  if (thread_test_start2_)
    155    tor_mutex_free(thread_test_start2_);
    156 }
    157 
    158 typedef struct cv_testinfo_t {
    159  tor_cond_t *cond;
    160  tor_mutex_t *mutex;
    161  int value;
    162  int addend;
    163  int shutdown;
    164  int n_shutdown;
    165  int n_wakeups;
    166  int n_timeouts;
    167  int n_threads;
    168  const struct timeval *tv;
    169 } cv_testinfo_t;
    170 
    171 static cv_testinfo_t *
    172 cv_testinfo_new(void)
    173 {
    174  cv_testinfo_t *i = tor_malloc_zero(sizeof(*i));
    175  i->cond = tor_cond_new();
    176  i->mutex = tor_mutex_new_nonrecursive();
    177  return i;
    178 }
    179 
    180 static void
    181 cv_testinfo_free(cv_testinfo_t *i)
    182 {
    183  if (!i)
    184    return;
    185  tor_cond_free(i->cond);
    186  tor_mutex_free(i->mutex);
    187  tor_free(i);
    188 }
    189 
    190 static void cv_test_thr_fn_(void *arg) ATTR_NORETURN;
    191 
    192 static void
    193 cv_test_thr_fn_(void *arg)
    194 {
    195  cv_testinfo_t *i = arg;
    196  int tid, r;
    197 
    198  tor_mutex_acquire(i->mutex);
    199  tid = i->n_threads++;
    200  tor_mutex_release(i->mutex);
    201  (void) tid;
    202 
    203  tor_mutex_acquire(i->mutex);
    204  while (1) {
    205    if (i->addend) {
    206      i->value += i->addend;
    207      i->addend = 0;
    208    }
    209 
    210    if (i->shutdown) {
    211      ++i->n_shutdown;
    212      i->shutdown = 0;
    213      tor_mutex_release(i->mutex);
    214      spawn_exit();
    215    }
    216    r = tor_cond_wait(i->cond, i->mutex, i->tv);
    217    ++i->n_wakeups;
    218    if (r == 1) {
    219      ++i->n_timeouts;
    220      tor_mutex_release(i->mutex);
    221      spawn_exit();
    222    }
    223  }
    224 }
    225 
    226 static void
    227 test_threads_conditionvar(void *arg)
    228 {
    229  cv_testinfo_t *ti=NULL;
    230  const struct timeval msec100 = { 0, 100*1000 };
    231  const int timeout = !strcmp(arg, "tv");
    232 
    233  ti = cv_testinfo_new();
    234  if (timeout) {
    235    ti->tv = &msec100;
    236  }
    237 
    238 #define SPIN_UNTIL(condition,sleep_msec)        \
    239  while (1) {                                   \
    240    tor_mutex_acquire(ti->mutex);               \
    241    if (condition) {                            \
    242      break;                                    \
    243    }                                           \
    244    tor_mutex_release(ti->mutex);               \
    245    tor_sleep_msec(sleep_msec);                 \
    246  }
    247 
    248  spawn_func(cv_test_thr_fn_, ti);
    249  spawn_func(cv_test_thr_fn_, ti);
    250  spawn_func(cv_test_thr_fn_, ti);
    251  spawn_func(cv_test_thr_fn_, ti);
    252 
    253  SPIN_UNTIL(ti->n_threads == 4, 10);
    254 
    255  time_t started_at = time(NULL);
    256 
    257  ti->addend = 7;
    258  ti->shutdown = 1;
    259  tor_cond_signal_one(ti->cond);
    260  tor_mutex_release(ti->mutex);
    261 
    262 #define SPIN()                                  \
    263  SPIN_UNTIL(ti->addend == 0, 0)
    264 
    265  SPIN();
    266 
    267  ti->addend = 30;
    268  ti->shutdown = 1;
    269  tor_cond_signal_all(ti->cond);
    270  tor_mutex_release(ti->mutex);
    271  SPIN();
    272 
    273  ti->addend = 1000;
    274  if (! timeout) ti->shutdown = 1;
    275  tor_cond_signal_one(ti->cond);
    276  tor_mutex_release(ti->mutex);
    277  SPIN();
    278  ti->addend = 300;
    279  if (! timeout) ti->shutdown = 1;
    280  tor_cond_signal_all(ti->cond);
    281  tor_mutex_release(ti->mutex);
    282 
    283  SPIN();
    284  tor_mutex_release(ti->mutex);
    285 
    286  tt_int_op(ti->value, OP_EQ, 1337);
    287  if (!timeout) {
    288    tt_int_op(ti->n_shutdown, OP_EQ, 4);
    289  } else {
    290    const int GIVE_UP_AFTER_SEC = 30;
    291    SPIN_UNTIL((ti->n_timeouts == 2 ||
    292                time(NULL) >= started_at + GIVE_UP_AFTER_SEC), 10);
    293    tt_int_op(ti->n_shutdown, OP_EQ, 2);
    294    tt_int_op(ti->n_timeouts, OP_EQ, 2);
    295    tor_mutex_release(ti->mutex);
    296  }
    297 
    298 done:
    299  cv_testinfo_free(ti);
    300 }
    301 
    302 #define THREAD_TEST(name)                                               \
    303  { #name, test_threads_##name, TT_FORK, NULL, NULL }
    304 
    305 struct testcase_t thread_tests[] = {
    306  THREAD_TEST(basic),
    307  { "conditionvar", test_threads_conditionvar, TT_FORK,
    308    &passthrough_setup, (void*)"no-tv" },
    309  { "conditionvar_timeout", test_threads_conditionvar, TT_FORK,
    310    &passthrough_setup, (void*)"tv" },
    311  END_OF_TESTCASES
    312 };