summaryrefslogtreecommitdiff
path: root/tests/t_mutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/t_mutex.c')
-rw-r--r--tests/t_mutex.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/tests/t_mutex.c b/tests/t_mutex.c
new file mode 100644
index 0000000..7c1463b
--- /dev/null
+++ b/tests/t_mutex.c
@@ -0,0 +1,138 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <pthread.h>
8#include <stdatomic.h>
9#include <string.h>
10#include <unistd.h>
11
12/* Opentracker */
13#include "ot_mutex.h"
14#include "test.h"
15
16/* Every announce passes through here, and what it has to get right is which
17 bucket a hash belongs to and that two threads never hold one bucket at
18 once. Both are checked against real mutexes rather than a stand-in. */
19
20/* A hash that lands in a chosen bucket: the bucket is the top
21 OT_BUCKET_COUNT_BITS of the first four bytes, read in network order */
22static void hash_for_bucket(ot_hash hash, int bucket) {
23 memset(hash, 0, sizeof(ot_hash));
24 hash[0] = (uint8_t)(bucket >> (OT_BUCKET_COUNT_BITS - 8));
25 hash[1] = (uint8_t)((bucket << (16 - OT_BUCKET_COUNT_BITS)) & 0xff);
26}
27
28static void case_a_hash_names_its_bucket(void) {
29 static const int buckets[] = {0, 1, 2, 17, 511, 512, OT_BUCKET_COUNT - 1};
30 size_t i;
31
32 test_case("a hash is served by the bucket its leading bits name");
33 for (i = 0; i < sizeof(buckets) / sizeof(buckets[0]); ++i) {
34 ot_hash hash;
35 ot_vector *by_hash, *by_number;
36
37 hash_for_bucket(hash, buckets[i]);
38 by_hash = mutex_bucket_lock_by_hash(hash);
39 mutex_bucket_unlock_by_hash(hash, 0);
40
41 by_number = mutex_bucket_lock(buckets[i]);
42 mutex_bucket_unlock(buckets[i], 0);
43
44 CHECK(by_hash == by_number);
45 }
46}
47
48static void case_every_bucket_is_its_own(void) {
49 ot_vector *first, *last, *middle;
50
51 test_case("no two buckets are the same one");
52 first = mutex_bucket_lock(0);
53 mutex_bucket_unlock(0, 0);
54 middle = mutex_bucket_lock(OT_BUCKET_COUNT / 2);
55 mutex_bucket_unlock(OT_BUCKET_COUNT / 2, 0);
56 last = mutex_bucket_lock(OT_BUCKET_COUNT - 1);
57 mutex_bucket_unlock(OT_BUCKET_COUNT - 1, 0);
58
59 CHECK(first != middle);
60 CHECK(middle != last);
61 CHECK(first != last);
62}
63
64static void case_the_count_follows_what_unlock_is_told(void) {
65 size_t before = mutex_get_torrent_count();
66
67 test_case("the torrent count moves by what unlocking reports, and not otherwise");
68 mutex_bucket_lock(3);
69 mutex_bucket_unlock(3, 0);
70 CHECK_INT((int)(mutex_get_torrent_count() - before), 0);
71
72 mutex_bucket_lock(3);
73 mutex_bucket_unlock(3, 5);
74 CHECK_INT((int)(mutex_get_torrent_count() - before), 5);
75
76 mutex_bucket_lock(3);
77 mutex_bucket_unlock(3, -2);
78 CHECK_INT((int)(mutex_get_torrent_count() - before), 3);
79}
80
81static _Atomic int g_other_got_in;
82
83static void *take_a_bucket(void *args) {
84 int bucket = (int)(intptr_t)args;
85
86 mutex_bucket_lock(bucket);
87 atomic_store(&g_other_got_in, 1);
88 mutex_bucket_unlock(bucket, 0);
89 return NULL;
90}
91
92static int waited_for_entry(int patience_ms) {
93 int waited;
94
95 for (waited = 0; waited < patience_ms && !atomic_load(&g_other_got_in); ++waited)
96 usleep(1000);
97 return atomic_load(&g_other_got_in);
98}
99
100static void case_a_held_bucket_keeps_others_out(void) {
101 pthread_t other;
102
103 test_case("a second thread waits for the bucket this one holds");
104 atomic_store(&g_other_got_in, 0);
105 mutex_bucket_lock(9);
106 CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)9), 0);
107
108 CHECK_INT(waited_for_entry(200), 0);
109
110 mutex_bucket_unlock(9, 0);
111 CHECK_INT(waited_for_entry(2000), 1);
112 pthread_join(other, NULL);
113}
114
115static void case_another_bucket_is_free_meanwhile(void) {
116 pthread_t other;
117
118 test_case("and a different bucket is not held up by it");
119 atomic_store(&g_other_got_in, 0);
120 mutex_bucket_lock(9);
121 CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)10), 0);
122
123 CHECK_INT(waited_for_entry(2000), 1);
124 pthread_join(other, NULL);
125 mutex_bucket_unlock(9, 0);
126}
127
128void test_mutex(void) {
129 mutex_init();
130
131 case_a_hash_names_its_bucket();
132 case_every_bucket_is_its_own();
133 case_the_count_follows_what_unlock_is_told();
134 case_a_held_bucket_keeps_others_out();
135 case_another_bucket_is_free_meanwhile();
136
137 mutex_deinit();
138}