From b6a4741a15371882a477dbc9a684faf6b39ec000 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sun, 1 Nov 2026 01:26:05 +0100 Subject: Add tests for the ot_mutex code, it didn't have any until now --- tests/runner.c | 1 + tests/t_mutex.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test.h | 1 + 3 files changed, 140 insertions(+) create mode 100644 tests/t_mutex.c (limited to 'tests') diff --git a/tests/runner.c b/tests/runner.c index 38bfb86..dcb009c 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -27,6 +27,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded}, {"ot_buf", test_buf}, {"ot_ip", test_ip}, {"ot_socket", test_socket}, + {"ot_mutex", test_mutex}, {"ot_workqueue", test_workqueue}, {"ot_batch", test_batch}, {"ot_fdstate", test_fdstate}, 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 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* System */ +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_mutex.h" +#include "test.h" + +/* Every announce passes through here, and what it has to get right is which + bucket a hash belongs to and that two threads never hold one bucket at + once. Both are checked against real mutexes rather than a stand-in. */ + +/* A hash that lands in a chosen bucket: the bucket is the top + OT_BUCKET_COUNT_BITS of the first four bytes, read in network order */ +static void hash_for_bucket(ot_hash hash, int bucket) { + memset(hash, 0, sizeof(ot_hash)); + hash[0] = (uint8_t)(bucket >> (OT_BUCKET_COUNT_BITS - 8)); + hash[1] = (uint8_t)((bucket << (16 - OT_BUCKET_COUNT_BITS)) & 0xff); +} + +static void case_a_hash_names_its_bucket(void) { + static const int buckets[] = {0, 1, 2, 17, 511, 512, OT_BUCKET_COUNT - 1}; + size_t i; + + test_case("a hash is served by the bucket its leading bits name"); + for (i = 0; i < sizeof(buckets) / sizeof(buckets[0]); ++i) { + ot_hash hash; + ot_vector *by_hash, *by_number; + + hash_for_bucket(hash, buckets[i]); + by_hash = mutex_bucket_lock_by_hash(hash); + mutex_bucket_unlock_by_hash(hash, 0); + + by_number = mutex_bucket_lock(buckets[i]); + mutex_bucket_unlock(buckets[i], 0); + + CHECK(by_hash == by_number); + } +} + +static void case_every_bucket_is_its_own(void) { + ot_vector *first, *last, *middle; + + test_case("no two buckets are the same one"); + first = mutex_bucket_lock(0); + mutex_bucket_unlock(0, 0); + middle = mutex_bucket_lock(OT_BUCKET_COUNT / 2); + mutex_bucket_unlock(OT_BUCKET_COUNT / 2, 0); + last = mutex_bucket_lock(OT_BUCKET_COUNT - 1); + mutex_bucket_unlock(OT_BUCKET_COUNT - 1, 0); + + CHECK(first != middle); + CHECK(middle != last); + CHECK(first != last); +} + +static void case_the_count_follows_what_unlock_is_told(void) { + size_t before = mutex_get_torrent_count(); + + test_case("the torrent count moves by what unlocking reports, and not otherwise"); + mutex_bucket_lock(3); + mutex_bucket_unlock(3, 0); + CHECK_INT((int)(mutex_get_torrent_count() - before), 0); + + mutex_bucket_lock(3); + mutex_bucket_unlock(3, 5); + CHECK_INT((int)(mutex_get_torrent_count() - before), 5); + + mutex_bucket_lock(3); + mutex_bucket_unlock(3, -2); + CHECK_INT((int)(mutex_get_torrent_count() - before), 3); +} + +static _Atomic int g_other_got_in; + +static void *take_a_bucket(void *args) { + int bucket = (int)(intptr_t)args; + + mutex_bucket_lock(bucket); + atomic_store(&g_other_got_in, 1); + mutex_bucket_unlock(bucket, 0); + return NULL; +} + +static int waited_for_entry(int patience_ms) { + int waited; + + for (waited = 0; waited < patience_ms && !atomic_load(&g_other_got_in); ++waited) + usleep(1000); + return atomic_load(&g_other_got_in); +} + +static void case_a_held_bucket_keeps_others_out(void) { + pthread_t other; + + test_case("a second thread waits for the bucket this one holds"); + atomic_store(&g_other_got_in, 0); + mutex_bucket_lock(9); + CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)9), 0); + + CHECK_INT(waited_for_entry(200), 0); + + mutex_bucket_unlock(9, 0); + CHECK_INT(waited_for_entry(2000), 1); + pthread_join(other, NULL); +} + +static void case_another_bucket_is_free_meanwhile(void) { + pthread_t other; + + test_case("and a different bucket is not held up by it"); + atomic_store(&g_other_got_in, 0); + mutex_bucket_lock(9); + CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)10), 0); + + CHECK_INT(waited_for_entry(2000), 1); + pthread_join(other, NULL); + mutex_bucket_unlock(9, 0); +} + +void test_mutex(void) { + mutex_init(); + + case_a_hash_names_its_bucket(); + case_every_bucket_is_its_own(); + case_the_count_follows_what_unlock_is_told(); + case_a_held_bucket_keeps_others_out(); + case_another_bucket_is_free_meanwhile(); + + mutex_deinit(); +} diff --git a/tests/test.h b/tests/test.h index d58742c..7582dab 100644 --- a/tests/test.h +++ b/tests/test.h @@ -86,6 +86,7 @@ void test_scan(void); void test_buf(void); void test_ip(void); void test_socket(void); +void test_mutex(void); void test_workqueue(void); void test_batch(void); void test_fdstate(void); -- cgit v1.3