summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-04-13 16:11:04 -0400
committerAndrew Opalach <andrew@akon.city> 2026-04-13 16:11:04 -0400
commita389b8f2f9c55b76fe5a28c3f1fea798ed37aa98 (patch)
treeff9dcee72485443b77f03220564efa44fbe0c705 /tests
parentf78a30aaccffddc3e3eab07870e02157322af956 (diff)
downloadlibnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.tar.gz
libnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.tar.bz2
libnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.zip
Allow packet_stream_reconnect() to cut offHEADmaster
a connecting stream. Cleanup timer. Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'tests')
-rw-r--r--tests/meson.build1
-rw-r--r--tests/timer_clear_pending.c158
2 files changed, 159 insertions, 0 deletions
diff --git a/tests/meson.build b/tests/meson.build
index 0ff6ffb..1074e40 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -3,6 +3,7 @@ executable('condition_variable', ['condition_variable.c'], dependencies: naunet)
executable('timer_thread', ['timer_thread.c'], dependencies: naunet)
if get_option('event-loop').enabled()
executable('timer_loop', ['timer_loop.c'], dependencies: naunet)
+ executable('timer_clear_pending', ['timer_clear_pending.c'], dependencies: naunet)
executable('loop_sleep', ['loop_sleep.c'], dependencies: naunet)
if not is_windows
executable('stdin_lines', ['stdin_lines.c'], dependencies: naunet)
diff --git a/tests/timer_clear_pending.c b/tests/timer_clear_pending.c
new file mode 100644
index 0000000..35eb3dd
--- /dev/null
+++ b/tests/timer_clear_pending.c
@@ -0,0 +1,158 @@
+#include <nnwt/common.h>
+#include <nnwt/thread.h>
+#include <nnwt/timer.h>
+#include <nnwt/signal.h>
+#include <nnwt/time.h>
+
+//#define TEST_WAIT_ON_SIGNAL
+
+static s32 s_quit = 0;
+static struct nn_event_loop s_loop;
+#ifndef TEST_WAIT_ON_SIGNAL
+static struct nn_timer s_timer;
+#endif
+static struct nn_signal s_signal;
+static struct nn_cond s_cond;
+static struct nn_mutex s_mutex;
+
+#ifndef TEST_WAIT_ON_SIGNAL
+static bool flip = false;
+static bool any_fail = false;
+static bool last_was_timeout = false;
+#endif
+static f64 last_tick;
+static u64 iter = 0;
+
+#define DELAY 0.002
+
+#ifndef TEST_WAIT_ON_SIGNAL
+static void timer_callback(void *userdata, struct nn_timer *timer)
+{
+ (void)userdata;
+ (void)timer;
+ if (!flip) {
+ al_printf("*******FAIL*******\n");
+ any_fail = true;
+ } else if (!any_fail && last_was_timeout) {
+ // Calling timer_stop() here could avoid this but it should
+ // realistically not happen at nearly any above 0 delay.
+ al_assert(false && "Unexpected repeat timeout, signal thread deadlocked or couldn't keep up?");
+ }
+ al_printf("Timeout iter #%llu (%.5f)\n", iter, nn_get_tick() - last_tick);
+ last_was_timeout = true;
+}
+#endif
+
+static void signal_callback(void *userdata)
+{
+ (void)userdata;
+ f64 tick = nn_get_tick();
+#ifndef TEST_WAIT_ON_SIGNAL
+ if (flip) {
+ if (last_was_timeout) {
+ last_was_timeout = false;
+ } else {
+ al_printf("Stopped iter #%llu (%.5f)\n", iter, tick - last_tick);
+ }
+ // The point of this test is to call timer_stop() right before it was set
+ // to fire. So we only care about the case where signal_callback() runs first.
+ // To do that we have to keep it close though, meaning they will trade back and forth.
+ nn_timer_stop(&s_timer);
+ // Now wait for ~DELAY seconds and if the timer still fires after being
+ // stopped, a fail case will trigger.
+ } else {
+ iter++;
+ nn_timer_again(&s_timer);
+ }
+ flip = !flip;
+#else
+ iter++;
+ al_printf("Signal(#%llu) (%.5f)\n", iter, tick - last_tick);
+#endif
+ nn_mutex_lock(&s_mutex);
+ nn_cond_signal(&s_cond);
+ last_tick = tick;
+ if (s_quit) {
+#ifndef TEST_WAIT_ON_SIGNAL
+ nn_timer_stop(&s_timer);
+#endif
+ nn_signal_stop(&s_signal);
+ }
+ nn_mutex_unlock(&s_mutex);
+}
+
+static nn_thread_result NNWT_THREADCALL signal_and_wait_thread(void *userdata)
+{
+ (void)userdata;
+ for (;;) {
+ // Locking before signal_send() is necessary. Otherwise there would be a race
+ // between this send being processed and calling cond_signal() on the loop thread
+ // before we get to cond_wait() in this thread.
+ nn_mutex_lock(&s_mutex);
+ nn_signal_send(&s_signal);
+ if (s_quit) break;
+ nn_cond_wait(&s_cond, &s_mutex);
+ f64 tick = last_tick;
+ nn_mutex_unlock(&s_mutex);
+#ifndef TEST_WAIT_ON_SIGNAL
+ nn_thread_sleep(MAX((tick + DELAY) - nn_get_tick(), 0.0));
+#else
+ (void)tick;
+#endif
+ }
+ nn_mutex_unlock(&s_mutex);
+ return 0;
+}
+
+static void sigint_handler(s32 signum)
+{
+ (void)signum;
+ nn_mutex_lock(&s_mutex);
+ if (s_quit) {
+ al_printf("If the console output was frozen (deadlock), test failed.\n");
+ exit(128 + SIGINT);
+ }
+ s_quit = 1;
+ al_printf("...^C again to force exit.\n");
+ nn_mutex_unlock(&s_mutex);
+}
+
+s32 main(void)
+{
+ if (!nn_common_init(NULL)) return EXIT_FAILURE;
+
+ signal(SIGINT, sigint_handler);
+
+ nn_event_loop_init(&s_loop);
+
+ nn_signal_init(&s_signal, &s_loop, signal_callback, NULL);
+ nn_signal_start(&s_signal);
+
+#ifndef TEST_WAIT_ON_SIGNAL
+ nn_timer_init(&s_timer, &s_loop, timer_callback, NULL);
+ nn_timer_set_repeat(&s_timer, DELAY);
+#endif
+ last_tick = nn_get_tick();
+
+ nn_mutex_init(&s_mutex);
+ nn_cond_init(&s_cond);
+
+ struct nn_thread thread;
+ nn_thread_create(&thread, signal_and_wait_thread, NULL);
+
+ nn_event_loop_run(&s_loop);
+ nn_event_loop_destroy(&s_loop);
+
+ nn_mutex_destroy(&s_mutex);
+ nn_cond_destroy(&s_cond);
+
+#ifndef TEST_WAIT_ON_SIGNAL
+ al_printf(any_fail ? "Test failed.\n" : "Test passed.\n");
+#else
+ al_printf("Test passed.\n");
+#endif
+
+ nn_common_close();
+
+ return EXIT_SUCCESS;
+}