Merge branch 'maint-0.4.5' into release-0.4.5
[tor.git] / src / test / test_threads.c
blobd5a1834aef185bfa82c0b2478be29cdf79b4d02a
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 */
6 #include "orconfig.h"
7 #include "core/or/or.h"
8 #include "lib/thread/threads.h"
9 #include "test/test.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_. */
37 static void
38 thread_test_func_(void* _s)
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();
57 tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
58 *cp = tor_strdup(buf);
60 tor_mutex_acquire(m);
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;
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_);
76 tor_free(mycount);
78 tor_mutex_release(m);
80 spawn_exit();
83 /** Run unit tests for threading logic. */
84 static void
85 test_threads_basic(void *arg)
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);
93 set_main_thread();
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;
117 tor_mutex_release(thread_test_mutex_);
118 /* Prevent the main thread from starving the worker threads. */
119 tor_sleep_msec(10);
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_);
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);
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);
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_);
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;
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();
177 return i;
180 static void
181 cv_testinfo_free(cv_testinfo_t *i)
183 if (!i)
184 return;
185 tor_cond_free(i->cond);
186 tor_mutex_free(i->mutex);
187 tor_free(i);
190 static void cv_test_thr_fn_(void *arg) ATTR_NORETURN;
192 static void
193 cv_test_thr_fn_(void *arg)
195 cv_testinfo_t *i = arg;
196 int tid, r;
198 tor_mutex_acquire(i->mutex);
199 tid = i->n_threads++;
200 tor_mutex_release(i->mutex);
201 (void) tid;
203 tor_mutex_acquire(i->mutex);
204 while (1) {
205 if (i->addend) {
206 i->value += i->addend;
207 i->addend = 0;
210 if (i->shutdown) {
211 ++i->n_shutdown;
212 i->shutdown = 0;
213 tor_mutex_release(i->mutex);
214 spawn_exit();
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();
226 static void
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();
234 if (timeout) {
235 ti->tv = &msec100;
238 #define SPIN_UNTIL(condition,sleep_msec) \
239 while (1) { \
240 tor_mutex_acquire(ti->mutex); \
241 if (condition) { \
242 break; \
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);
257 ti->addend = 7;
258 ti->shutdown = 1;
259 tor_cond_signal_one(ti->cond);
260 tor_mutex_release(ti->mutex);
262 #define SPIN() \
263 SPIN_UNTIL(ti->addend == 0, 0)
265 SPIN();
267 ti->addend = 30;
268 ti->shutdown = 1;
269 tor_cond_signal_all(ti->cond);
270 tor_mutex_release(ti->mutex);
271 SPIN();
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);
283 SPIN();
284 tor_mutex_release(ti->mutex);
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);
298 done:
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[] = {
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