diff options
| author | 2026-04-13 16:11:04 -0400 | |
|---|---|---|
| committer | 2026-04-13 16:11:04 -0400 | |
| commit | a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98 (patch) | |
| tree | ff9dcee72485443b77f03220564efa44fbe0c705 /tests | |
| parent | f78a30aaccffddc3e3eab07870e02157322af956 (diff) | |
| download | libnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.tar.gz libnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.tar.bz2 libnaunet-a389b8f2f9c55b76fe5a28c3f1fea798ed37aa98.zip | |
a connecting stream. Cleanup timer.
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/meson.build | 1 | ||||
| -rw-r--r-- | tests/timer_clear_pending.c | 158 |
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; +} |