LCOV - code coverage report
Current view: top level - test - test_threads.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 160 165 97.0 %
Date: 2021-11-24 03:28:48 Functions: 6 6 100.0 %

          Line data    Source code
       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           2 : thread_test_func_(void* _s)
      39             : {
      40           2 :   char *s = _s;
      41           2 :   int i;
      42           2 :   tor_mutex_t *m;
      43           2 :   char buf[64];
      44           2 :   char **cp;
      45           2 :   int *mycount = tor_malloc_zero(sizeof(int));
      46           2 :   tor_threadlocal_set(&count, mycount);
      47           2 :   if (!strcmp(s, "thread 1")) {
      48           1 :     m = thread_test_start1_;
      49           1 :     cp = &thread1_name_;
      50           1 :     thread_fn_tid1 = tor_get_thread_id();
      51             :   } else {
      52           1 :     m = thread_test_start2_;
      53           1 :     cp = &thread2_name_;
      54           1 :     thread_fn_tid2 = tor_get_thread_id();
      55             :   }
      56             : 
      57           2 :   tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
      58           2 :   *cp = tor_strdup(buf);
      59             : 
      60           2 :   tor_mutex_acquire(m);
      61             : 
      62       20001 :   for (i=0; i<10000; ++i) {
      63       19997 :     tor_mutex_acquire(thread_test_mutex_);
      64       20000 :     strmap_set(thread_test_strmap_, "last to run", *cp);
      65       20000 :     tor_mutex_release(thread_test_mutex_);
      66       19997 :     int *tls_count = tor_threadlocal_get(&count);
      67       19997 :     tor_assert(tls_count == mycount);
      68       19997 :     ++*tls_count;
      69             :   }
      70           2 :   tor_mutex_acquire(thread_test_mutex_);
      71           2 :   strmap_set(thread_test_strmap_, s, *cp);
      72           2 :   if (in_main_thread())
      73           0 :     ++thread_fns_failed;
      74           2 :   tor_mutex_release(thread_test_mutex_);
      75             : 
      76           2 :   tor_free(mycount);
      77             : 
      78           2 :   tor_mutex_release(m);
      79             : 
      80           2 :   spawn_exit();
      81             : }
      82             : 
      83             : /** Run unit tests for threading logic. */
      84             : static void
      85           1 : test_threads_basic(void *arg)
      86             : {
      87           1 :   char *s1 = NULL, *s2 = NULL;
      88           1 :   int done = 0, timedout = 0;
      89           1 :   time_t started;
      90           1 :   (void) arg;
      91           1 :   tt_int_op(tor_threadlocal_init(&count), OP_EQ, 0);
      92             : 
      93           1 :   set_main_thread();
      94             : 
      95           1 :   thread_test_mutex_ = tor_mutex_new();
      96           1 :   thread_test_start1_ = tor_mutex_new();
      97           1 :   thread_test_start2_ = tor_mutex_new();
      98           1 :   thread_test_strmap_ = strmap_new();
      99           1 :   s1 = tor_strdup("thread 1");
     100           1 :   s2 = tor_strdup("thread 2");
     101           1 :   tor_mutex_acquire(thread_test_start1_);
     102           1 :   tor_mutex_acquire(thread_test_start2_);
     103           1 :   spawn_func(thread_test_func_, s1);
     104           1 :   spawn_func(thread_test_func_, s2);
     105           1 :   tor_mutex_release(thread_test_start2_);
     106           1 :   tor_mutex_release(thread_test_start1_);
     107           1 :   started = time(NULL);
     108           3 :   while (!done) {
     109           2 :     tor_mutex_acquire(thread_test_mutex_);
     110           2 :     strmap_assert_ok(thread_test_strmap_);
     111           3 :     if (strmap_get(thread_test_strmap_, "thread 1") &&
     112           1 :         strmap_get(thread_test_strmap_, "thread 2")) {
     113             :       done = 1;
     114           1 :     } else if (time(NULL) > started + 150) {
     115           0 :       timedout = done = 1;
     116             :     }
     117           2 :     tor_mutex_release(thread_test_mutex_);
     118             :     /* Prevent the main thread from starving the worker threads. */
     119           2 :     tor_sleep_msec(10);
     120             :   }
     121           1 :   tor_mutex_acquire(thread_test_start1_);
     122           1 :   tor_mutex_release(thread_test_start1_);
     123           1 :   tor_mutex_acquire(thread_test_start2_);
     124           1 :   tor_mutex_release(thread_test_start2_);
     125             : 
     126           1 :   tor_mutex_free(thread_test_mutex_);
     127             : 
     128           1 :   if (timedout) {
     129           0 :     tt_assert(strmap_get(thread_test_strmap_, "thread 1"));
     130           0 :     tt_assert(strmap_get(thread_test_strmap_, "thread 2"));
     131           0 :     tt_assert(!timedout);
     132             :   }
     133             : 
     134             :   /* different thread IDs. */
     135           1 :   tt_assert(strcmp(strmap_get(thread_test_strmap_, "thread 1"),
     136             :                      strmap_get(thread_test_strmap_, "thread 2")));
     137           1 :   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           1 :   tt_int_op(thread_fns_failed, OP_EQ, 0);
     143           1 :   tt_int_op(thread_fn_tid1, OP_NE, thread_fn_tid2);
     144             : 
     145           1 :  done:
     146           1 :   tor_free(s1);
     147           1 :   tor_free(s2);
     148           1 :   tor_free(thread1_name_);
     149           1 :   tor_free(thread2_name_);
     150           1 :   if (thread_test_strmap_)
     151           1 :     strmap_free(thread_test_strmap_, NULL);
     152           1 :   if (thread_test_start1_)
     153           1 :     tor_mutex_free(thread_test_start1_);
     154           1 :   if (thread_test_start2_)
     155           1 :     tor_mutex_free(thread_test_start2_);
     156           1 : }
     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           2 : cv_testinfo_new(void)
     173             : {
     174           2 :   cv_testinfo_t *i = tor_malloc_zero(sizeof(*i));
     175           2 :   i->cond = tor_cond_new();
     176           2 :   i->mutex = tor_mutex_new_nonrecursive();
     177           2 :   return i;
     178             : }
     179             : 
     180             : static void
     181           2 : cv_testinfo_free(cv_testinfo_t *i)
     182             : {
     183           2 :   if (!i)
     184             :     return;
     185           2 :   tor_cond_free(i->cond);
     186           2 :   tor_mutex_free(i->mutex);
     187           2 :   tor_free(i);
     188             : }
     189             : 
     190             : static void cv_test_thr_fn_(void *arg) ATTR_NORETURN;
     191             : 
     192             : static void
     193           8 : cv_test_thr_fn_(void *arg)
     194             : {
     195           8 :   cv_testinfo_t *i = arg;
     196           8 :   int tid, r;
     197             : 
     198           8 :   tor_mutex_acquire(i->mutex);
     199           8 :   tid = i->n_threads++;
     200           8 :   tor_mutex_release(i->mutex);
     201           8 :   (void) tid;
     202             : 
     203           8 :   tor_mutex_acquire(i->mutex);
     204          21 :   while (1) {
     205          21 :     if (i->addend) {
     206           8 :       i->value += i->addend;
     207           8 :       i->addend = 0;
     208             :     }
     209             : 
     210          21 :     if (i->shutdown) {
     211           6 :       ++i->n_shutdown;
     212           6 :       i->shutdown = 0;
     213           6 :       tor_mutex_release(i->mutex);
     214           6 :       spawn_exit();
     215             :     }
     216          15 :     r = tor_cond_wait(i->cond, i->mutex, i->tv);
     217          15 :     ++i->n_wakeups;
     218          15 :     if (r == 1) {
     219           2 :       ++i->n_timeouts;
     220           2 :       tor_mutex_release(i->mutex);
     221           2 :       spawn_exit();
     222             :     }
     223             :   }
     224             : }
     225             : 
     226             : static void
     227           2 : test_threads_conditionvar(void *arg)
     228             : {
     229           2 :   cv_testinfo_t *ti=NULL;
     230           2 :   const struct timeval msec100 = { 0, 100*1000 };
     231           2 :   const int timeout = !strcmp(arg, "tv");
     232             : 
     233           2 :   ti = cv_testinfo_new();
     234           2 :   if (timeout) {
     235           1 :     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           2 :   spawn_func(cv_test_thr_fn_, ti);
     249           2 :   spawn_func(cv_test_thr_fn_, ti);
     250           2 :   spawn_func(cv_test_thr_fn_, ti);
     251           2 :   spawn_func(cv_test_thr_fn_, ti);
     252             : 
     253           4 :   SPIN_UNTIL(ti->n_threads == 4, 10);
     254             : 
     255           2 :   time_t started_at = time(NULL);
     256             : 
     257           2 :   ti->addend = 7;
     258           2 :   ti->shutdown = 1;
     259           2 :   tor_cond_signal_one(ti->cond);
     260           2 :   tor_mutex_release(ti->mutex);
     261             : 
     262             : #define SPIN()                                  \
     263             :   SPIN_UNTIL(ti->addend == 0, 0)
     264             : 
     265           4 :   SPIN();
     266             : 
     267           2 :   ti->addend = 30;
     268           2 :   ti->shutdown = 1;
     269           2 :   tor_cond_signal_all(ti->cond);
     270           2 :   tor_mutex_release(ti->mutex);
     271           4 :   SPIN();
     272             : 
     273           2 :   ti->addend = 1000;
     274           2 :   if (! timeout) ti->shutdown = 1;
     275           2 :   tor_cond_signal_one(ti->cond);
     276           2 :   tor_mutex_release(ti->mutex);
     277           4 :   SPIN();
     278           2 :   ti->addend = 300;
     279           2 :   if (! timeout) ti->shutdown = 1;
     280           2 :   tor_cond_signal_all(ti->cond);
     281           2 :   tor_mutex_release(ti->mutex);
     282             : 
     283           4 :   SPIN();
     284           2 :   tor_mutex_release(ti->mutex);
     285             : 
     286           2 :   tt_int_op(ti->value, OP_EQ, 1337);
     287           2 :   if (!timeout) {
     288           1 :     tt_int_op(ti->n_shutdown, OP_EQ, 4);
     289             :   } else {
     290             :     const int GIVE_UP_AFTER_SEC = 30;
     291          11 :     SPIN_UNTIL((ti->n_timeouts == 2 ||
     292           1 :                 time(NULL) >= started_at + GIVE_UP_AFTER_SEC), 10);
     293           1 :     tt_int_op(ti->n_shutdown, OP_EQ, 2);
     294           1 :     tt_int_op(ti->n_timeouts, OP_EQ, 2);
     295           1 :     tor_mutex_release(ti->mutex);
     296             :   }
     297             : 
     298           2 :  done:
     299           2 :   cv_testinfo_free(ti);
     300           2 : }
     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             : };

Generated by: LCOV version 1.14