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.c171
1 files changed, 101 insertions, 70 deletions
diff --git a/tests/t_mutex.c b/tests/t_mutex.c
index 3307d70..7c1463b 100644
--- a/tests/t_mutex.c
+++ b/tests/t_mutex.c
@@ -4,104 +4,135 @@
4 $id$ */ 4 $id$ */
5 5
6/* System */ 6/* System */
7#include <stdlib.h> 7#include <pthread.h>
8#include <stdatomic.h>
8#include <string.h> 9#include <string.h>
10#include <unistd.h>
9 11
10/* Opentracker */ 12/* Opentracker */
11#include "ot_mutex.h" 13#include "ot_mutex.h"
12#include "test.h" 14#include "test.h"
13 15
14/* A worker asks for a class of task and blocks until one turns up, so a task 16/* Every announce passes through here, and what it has to get right is which
15 whose class matches no worker is never claimed and whoever is waiting for 17 bucket a hash belongs to and that two threads never hold one bucket at
16 its answer waits forever. These cases pin the matching. */ 18 once. Both are checked against real mutexes rather than a stand-in. */
17 19
18static void case_task_goes_to_its_own_class(void) { 20/* A hash that lands in a chosen bucket: the bucket is the top
19 ot_tasktype asked; 21 OT_BUCKET_COUNT_BITS of the first four bytes, read in network order */
20 ot_taskid taskid; 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);
21 40
22 test_case("a worker is handed the task of the class it asked for"); 41 by_number = mutex_bucket_lock(buckets[i]);
23 CHECK_INT(mutex_workqueue_pushtask(41, TASK_FULLSCRAPE_TPB_ASCII), 0); 42 mutex_bucket_unlock(buckets[i], 0);
24 asked = TASK_FULLSCRAPE;
25 taskid = mutex_workqueue_poptask(&asked);
26 CHECK(taskid != 0);
27 43
28 test_case("and the full task type, not just its class"); 44 CHECK(by_hash == by_number);
29 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_ASCII); 45 }
30 mutex_workqueue_canceltask(41);
31} 46}
32 47
33static void case_flags_survive_the_queue(void) { 48static void case_every_bucket_is_its_own(void) {
34 ot_tasktype asked; 49 ot_vector *first, *last, *middle;
35 50
36 test_case("flags outside the class mask are carried through"); 51 test_case("no two buckets are the same one");
37 CHECK_INT(mutex_workqueue_pushtask(42, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP), 0); 52 first = mutex_bucket_lock(0);
38 asked = TASK_FULLSCRAPE; 53 mutex_bucket_unlock(0, 0);
39 CHECK(mutex_workqueue_poptask(&asked) != 0); 54 middle = mutex_bucket_lock(OT_BUCKET_COUNT / 2);
40 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_URLENCODED | TASK_FLAG_GZIP); 55 mutex_bucket_unlock(OT_BUCKET_COUNT / 2, 0);
41 mutex_workqueue_canceltask(42); 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);
42} 62}
43 63
44static void case_classes_do_not_cross(void) { 64static void case_the_count_follows_what_unlock_is_told(void) {
45 ot_tasktype asked; 65 size_t before = mutex_get_torrent_count();
46 ot_taskid taskid; 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);
47 71
48 test_case("a stats worker passes over a fullscrape task"); 72 mutex_bucket_lock(3);
49 CHECK_INT(mutex_workqueue_pushtask(43, TASK_FULLSCRAPE_TPB_BINARY), 0); 73 mutex_bucket_unlock(3, 5);
50 CHECK_INT(mutex_workqueue_pushtask(44, TASK_STATS_PEERS), 0); 74 CHECK_INT((int)(mutex_get_torrent_count() - before), 5);
51 75
52 asked = TASK_STATS; 76 mutex_bucket_lock(3);
53 taskid = mutex_workqueue_poptask(&asked); 77 mutex_bucket_unlock(3, -2);
54 CHECK(taskid != 0); 78 CHECK_INT((int)(mutex_get_torrent_count() - before), 3);
55 CHECK_INT(asked, TASK_STATS_PEERS); 79}
56 80
57 test_case("and leaves it for the one that wants it"); 81static _Atomic int g_other_got_in;
58 asked = TASK_FULLSCRAPE;
59 taskid = mutex_workqueue_poptask(&asked);
60 CHECK(taskid != 0);
61 CHECK_INT(asked, TASK_FULLSCRAPE_TPB_BINARY);
62 82
63 mutex_workqueue_canceltask(43); 83static void *take_a_bucket(void *args) {
64 mutex_workqueue_canceltask(44); 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);
65} 98}
66 99
67static void case_every_task_gets_its_own_id(void) { 100static void case_a_held_bucket_keeps_others_out(void) {
68 ot_tasktype asked; 101 pthread_t other;
69 ot_taskid first, second;
70 102
71 test_case("two queued tasks are handed out under different ids"); 103 test_case("a second thread waits for the bucket this one holds");
72 CHECK_INT(mutex_workqueue_pushtask(45, TASK_FULLSCRAPE), 0); 104 atomic_store(&g_other_got_in, 0);
73 CHECK_INT(mutex_workqueue_pushtask(46, TASK_FULLSCRAPE), 0); 105 mutex_bucket_lock(9);
106 CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)9), 0);
74 107
75 asked = TASK_FULLSCRAPE; 108 CHECK_INT(waited_for_entry(200), 0);
76 first = mutex_workqueue_poptask(&asked);
77 asked = TASK_FULLSCRAPE;
78 second = mutex_workqueue_poptask(&asked);
79 CHECK(first != 0);
80 CHECK(second != 0);
81 CHECK(first != second);
82 109
83 mutex_workqueue_canceltask(45); 110 mutex_bucket_unlock(9, 0);
84 mutex_workqueue_canceltask(46); 111 CHECK_INT(waited_for_entry(2000), 1);
112 pthread_join(other, NULL);
85} 113}
86 114
87static void case_cancelled_task_is_not_handed_out(void) { 115static void case_another_bucket_is_free_meanwhile(void) {
88 ot_tasktype asked; 116 pthread_t other;
89 117
90 test_case("a cancelled task is gone before any worker sees it"); 118 test_case("and a different bucket is not held up by it");
91 CHECK_INT(mutex_workqueue_pushtask(47, TASK_FULLSCRAPE_TPB_ASCII_PLUS), 0); 119 atomic_store(&g_other_got_in, 0);
92 CHECK_INT(mutex_workqueue_pushtask(48, TASK_FULLSCRAPE), 0); 120 mutex_bucket_lock(9);
93 mutex_workqueue_canceltask(47); 121 CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)10), 0);
94 122
95 asked = TASK_FULLSCRAPE; 123 CHECK_INT(waited_for_entry(2000), 1);
96 CHECK(mutex_workqueue_poptask(&asked) != 0); 124 pthread_join(other, NULL);
97 CHECK_INT(asked, TASK_FULLSCRAPE); 125 mutex_bucket_unlock(9, 0);
98 mutex_workqueue_canceltask(48);
99} 126}
100 127
101void test_mutex(void) { 128void test_mutex(void) {
102 case_task_goes_to_its_own_class(); 129 mutex_init();
103 case_flags_survive_the_queue(); 130
104 case_classes_do_not_cross(); 131 case_a_hash_names_its_bucket();
105 case_every_task_gets_its_own_id(); 132 case_every_bucket_is_its_own();
106 case_cancelled_task_is_not_handed_out(); 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();
107} 138}