1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2020, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
7 #include "core/or/or.h"
8 #include "lib/thread/threads.h"
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
;
24 static int thread_fns_failed
= 0;
26 static unsigned long thread_fn_tid1
, thread_fn_tid2
;
28 static void thread_test_func_(void* _s
) ATTR_NORETURN
;
30 /** How many iterations have the threads in the unit test run? */
31 static tor_threadlocal_t count
;
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_. */
38 thread_test_func_(void* _s
)
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_
;
50 thread_fn_tid1
= tor_get_thread_id();
52 m
= thread_test_start2_
;
54 thread_fn_tid2
= tor_get_thread_id();
57 tor_snprintf(buf
, sizeof(buf
), "%lu", tor_get_thread_id());
58 *cp
= tor_strdup(buf
);
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
);
70 tor_mutex_acquire(thread_test_mutex_
);
71 strmap_set(thread_test_strmap_
, s
, *cp
);
74 tor_mutex_release(thread_test_mutex_
);
83 /** Run unit tests for threading logic. */
85 test_threads_basic(void *arg
)
87 char *s1
= NULL
, *s2
= NULL
;
88 int done
= 0, timedout
= 0;
91 tt_int_op(tor_threadlocal_init(&count
), OP_EQ
, 0);
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
);
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")) {
114 } else if (time(NULL
) > started
+ 150) {
117 tor_mutex_release(thread_test_mutex_
);
118 /* Prevent the main thread from starving the worker threads. */
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_
);
126 tor_mutex_free(thread_test_mutex_
);
129 tt_assert(strmap_get(thread_test_strmap_
, "thread 1"));
130 tt_assert(strmap_get(thread_test_strmap_
, "thread 2"));
131 tt_assert(!timedout
);
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")));
142 tt_int_op(thread_fns_failed
, OP_EQ
, 0);
143 tt_int_op(thread_fn_tid1
, OP_NE
, thread_fn_tid2
);
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_
);
158 typedef struct cv_testinfo_t
{
168 const struct timeval
*tv
;
171 static cv_testinfo_t
*
172 cv_testinfo_new(void)
174 cv_testinfo_t
*i
= tor_malloc_zero(sizeof(*i
));
175 i
->cond
= tor_cond_new();
176 i
->mutex
= tor_mutex_new_nonrecursive();
181 cv_testinfo_free(cv_testinfo_t
*i
)
185 tor_cond_free(i
->cond
);
186 tor_mutex_free(i
->mutex
);
190 static void cv_test_thr_fn_(void *arg
) ATTR_NORETURN
;
193 cv_test_thr_fn_(void *arg
)
195 cv_testinfo_t
*i
= arg
;
198 tor_mutex_acquire(i
->mutex
);
199 tid
= i
->n_threads
++;
200 tor_mutex_release(i
->mutex
);
203 tor_mutex_acquire(i
->mutex
);
206 i
->value
+= i
->addend
;
213 tor_mutex_release(i
->mutex
);
216 r
= tor_cond_wait(i
->cond
, i
->mutex
, i
->tv
);
220 tor_mutex_release(i
->mutex
);
227 test_threads_conditionvar(void *arg
)
229 cv_testinfo_t
*ti
=NULL
;
230 const struct timeval msec100
= { 0, 100*1000 };
231 const int timeout
= !strcmp(arg
, "tv");
233 ti
= cv_testinfo_new();
238 #define SPIN_UNTIL(condition,sleep_msec) \
240 tor_mutex_acquire(ti->mutex); \
244 tor_mutex_release(ti->mutex); \
245 tor_sleep_msec(sleep_msec); \
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
);
253 SPIN_UNTIL(ti
->n_threads
== 4, 10);
255 time_t started_at
= time(NULL
);
259 tor_cond_signal_one(ti
->cond
);
260 tor_mutex_release(ti
->mutex
);
263 SPIN_UNTIL(ti->addend == 0, 0)
269 tor_cond_signal_all(ti
->cond
);
270 tor_mutex_release(ti
->mutex
);
274 if (! timeout
) ti
->shutdown
= 1;
275 tor_cond_signal_one(ti
->cond
);
276 tor_mutex_release(ti
->mutex
);
279 if (! timeout
) ti
->shutdown
= 1;
280 tor_cond_signal_all(ti
->cond
);
281 tor_mutex_release(ti
->mutex
);
284 tor_mutex_release(ti
->mutex
);
286 tt_int_op(ti
->value
, OP_EQ
, 1337);
288 tt_int_op(ti
->n_shutdown
, OP_EQ
, 4);
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
);
299 cv_testinfo_free(ti
);
302 #define THREAD_TEST(name) \
303 { #name, test_threads_##name, TT_FORK, NULL, NULL }
305 struct testcase_t thread_tests
[] = {
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" },