aboutsummaryrefslogtreecommitdiff
path: root/src/test/test_threads.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2013-09-25 14:50:01 -0400
committerNick Mathewson <nickm@torproject.org>2015-01-14 11:09:47 -0500
commit74b782645ad89b8bd5a18fd3f2cb3be45c38a04f (patch)
treedbe86794772966ddcbc65c970d0b74a0bdbba16b /src/test/test_threads.c
parentc51f7c23e3af71466f9bd2ae57ae7a2b998ee3e2 (diff)
downloadtor-74b782645ad89b8bd5a18fd3f2cb3be45c38a04f.tar.gz
tor-74b782645ad89b8bd5a18fd3f2cb3be45c38a04f.zip
Move thread tests into their own module
Diffstat (limited to 'src/test/test_threads.c')
-rw-r--r--src/test/test_threads.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
new file mode 100644
index 0000000000..2b4c93393f
--- /dev/null
+++ b/src/test/test_threads.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "compat_threads.h"
+#include "test.h"
+
+/** mutex for thread test to stop the threads hitting data at the same time. */
+static tor_mutex_t *thread_test_mutex_ = NULL;
+/** mutexes for the thread test to make sure that the threads have to
+ * interleave somewhat. */
+static tor_mutex_t *thread_test_start1_ = NULL,
+ *thread_test_start2_ = NULL;
+/** Shared strmap for the thread test. */
+static strmap_t *thread_test_strmap_ = NULL;
+/** The name of thread1 for the thread test */
+static char *thread1_name_ = NULL;
+/** The name of thread2 for the thread test */
+static char *thread2_name_ = NULL;
+
+static void thread_test_func_(void* _s) ATTR_NORETURN;
+
+/** How many iterations have the threads in the unit test run? */
+static int t1_count = 0, t2_count = 0;
+
+/** Helper function for threading unit tests: This function runs in a
+ * subthread. It grabs its own mutex (start1 or start2) to make sure that it
+ * should start, then it repeatedly alters _test_thread_strmap protected by
+ * thread_test_mutex_. */
+static void
+thread_test_func_(void* _s)
+{
+ char *s = _s;
+ int i, *count;
+ tor_mutex_t *m;
+ char buf[64];
+ char **cp;
+ if (!strcmp(s, "thread 1")) {
+ m = thread_test_start1_;
+ cp = &thread1_name_;
+ count = &t1_count;
+ } else {
+ m = thread_test_start2_;
+ cp = &thread2_name_;
+ count = &t2_count;
+ }
+
+ tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
+ *cp = tor_strdup(buf);
+
+ tor_mutex_acquire(m);
+
+ for (i=0; i<10000; ++i) {
+ tor_mutex_acquire(thread_test_mutex_);
+ strmap_set(thread_test_strmap_, "last to run", *cp);
+ ++*count;
+ tor_mutex_release(thread_test_mutex_);
+ }
+ tor_mutex_acquire(thread_test_mutex_);
+ strmap_set(thread_test_strmap_, s, *cp);
+ tor_mutex_release(thread_test_mutex_);
+
+ tor_mutex_release(m);
+
+ spawn_exit();
+}
+
+/** Run unit tests for threading logic. */
+static void
+test_threads_basic(void *arg)
+{
+ char *s1 = NULL, *s2 = NULL;
+ int done = 0, timedout = 0;
+ time_t started;
+#ifndef _WIN32
+ struct timeval tv;
+ tv.tv_sec=0;
+ tv.tv_usec=100*1000;
+#endif
+ (void)arg;
+ thread_test_mutex_ = tor_mutex_new();
+ thread_test_start1_ = tor_mutex_new();
+ thread_test_start2_ = tor_mutex_new();
+ thread_test_strmap_ = strmap_new();
+ s1 = tor_strdup("thread 1");
+ s2 = tor_strdup("thread 2");
+ tor_mutex_acquire(thread_test_start1_);
+ tor_mutex_acquire(thread_test_start2_);
+ spawn_func(thread_test_func_, s1);
+ spawn_func(thread_test_func_, s2);
+ tor_mutex_release(thread_test_start2_);
+ tor_mutex_release(thread_test_start1_);
+ started = time(NULL);
+ while (!done) {
+ tor_mutex_acquire(thread_test_mutex_);
+ strmap_assert_ok(thread_test_strmap_);
+ if (strmap_get(thread_test_strmap_, "thread 1") &&
+ strmap_get(thread_test_strmap_, "thread 2")) {
+ done = 1;
+ } else if (time(NULL) > started + 150) {
+ timedout = done = 1;
+ }
+ tor_mutex_release(thread_test_mutex_);
+#ifndef _WIN32
+ /* Prevent the main thread from starving the worker threads. */
+ select(0, NULL, NULL, NULL, &tv);
+#endif
+ }
+ tor_mutex_acquire(thread_test_start1_);
+ tor_mutex_release(thread_test_start1_);
+ tor_mutex_acquire(thread_test_start2_);
+ tor_mutex_release(thread_test_start2_);
+
+ tor_mutex_free(thread_test_mutex_);
+
+ if (timedout) {
+ printf("\nTimed out: %d %d", t1_count, t2_count);
+ tt_assert(strmap_get(thread_test_strmap_, "thread 1"));
+ tt_assert(strmap_get(thread_test_strmap_, "thread 2"));
+ tt_assert(!timedout);
+ }
+
+ /* different thread IDs. */
+ tt_assert(strcmp(strmap_get(thread_test_strmap_, "thread 1"),
+ strmap_get(thread_test_strmap_, "thread 2")));
+ tt_assert(!strcmp(strmap_get(thread_test_strmap_, "thread 1"),
+ strmap_get(thread_test_strmap_, "last to run")) ||
+ !strcmp(strmap_get(thread_test_strmap_, "thread 2"),
+ strmap_get(thread_test_strmap_, "last to run")));
+
+ done:
+ tor_free(s1);
+ tor_free(s2);
+ tor_free(thread1_name_);
+ tor_free(thread2_name_);
+ if (thread_test_strmap_)
+ strmap_free(thread_test_strmap_, NULL);
+ if (thread_test_start1_)
+ tor_mutex_free(thread_test_start1_);
+ if (thread_test_start2_)
+ tor_mutex_free(thread_test_start2_);
+}
+
+#define THREAD_TEST(name) \
+ { #name, test_threads_##name, TT_FORK, NULL, NULL }
+
+struct testcase_t thread_tests[] = {
+ THREAD_TEST(basic),
+ END_OF_TESTCASES
+};
+