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