diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-15 20:38:14 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-15 20:38:14 +0300 |
| commit | c2832a63547e6328afde25bb3b8145267eadef47 (patch) | |
| tree | bf03ace79f01df10ddaffd70bea74678ab4ca219 /tests/common/test_timers.c | |
| parent | 3d012d707d0242780cd386868ab511846dc9b0af (diff) | |
Make the loop public and let the display drive it
An application that has its own descriptors and timers needed a second
loop next to yasl's, and could not see yasl's key-repeat timer, clipboard
pipes or reconnect timer. Now there is one wait: the application owns a
yasl_loop, and yasl_init can run the display on it.
- Public yasl_loop_*: descriptors, timers and signals. Descriptor
callbacks return void. Timer IDs are 64 bits and never reused; a zero
delay runs in the next step. A nested step or run fails with EBUSY, a
step off the loop's thread with EPERM, and a quit made before a run ends
it at once. An interrupted wait keeps its deadline. Creating a loop
fails when it cannot make its wake pipe.
- The display drives the loop through prepare, wait and check hooks
instead of wrapping its step. Wayland finishes its read before any
callback runs, so loop callbacks may create surfaces and do roundtrips.
Readiness (queued events, an owed REFRESH) is derived in the step, so
the wait never blocks over pending work. A draw that cannot proceed for
want of a buffer is no longer counted as work.
- yasl_init takes struct yasl_options. With loop and on_event, each step
delivers events to on_event and yasl_wait_event fails with EBUSY.
EALREADY only for the same options, EBUSY for others.
- yasl_surface_request_frame: a REFRESH once the window can show a new
frame, for pacing animation.
- DISPLAY_LOST reports whether the display reconnects. Pull waits still
fail with ENOTCONN after a final loss.
- Events pushed from another thread wake the loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/common/test_timers.c')
| -rw-r--r-- | tests/common/test_timers.c | 128 |
1 files changed, 86 insertions, 42 deletions
diff --git a/tests/common/test_timers.c b/tests/common/test_timers.c index 2c3542f..3a35819 100644 --- a/tests/common/test_timers.c +++ b/tests/common/test_timers.c @@ -3,15 +3,17 @@ #include "common/types.h" #include <assert.h> +#include <signal.h> #include <stdio.h> #include <string.h> +#include <unistd.h> struct test_ctx { int count; uint64_t last_time; yasl_private_timer_id id; yasl_private_timer_id other_id; - struct yasl_private_loop *loop; + struct yasl_loop *loop; struct yasl_surface *surface; bool done; }; @@ -21,11 +23,32 @@ static uint64_t oneshot_cb(void *ud, yasl_private_timer_id id, uint64_t interval { struct test_ctx *ctx = ud; assert(id == ctx->id); - assert(interval_ns > 0); + (void)interval_ns; ctx->count++; return 0; /* Disarm */ } +static uint64_t count_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) +{ + (void)id; (void)interval_ns; + (*(int *)ud)++; + return 0; +} + +/* Adds enough timers to move the timer array, then repeats itself. */ +static uint64_t grow_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) +{ + struct test_ctx *ctx = ud; + assert(id == ctx->id); + if (ctx->count++ == 0) { + for (int i = 0; i < 64; i++) + assert(yasl_loop_add_timer(ctx->loop, YASL_PRIVATE_MS_TO_NS(60000), count_cb, NULL)); + return YASL_PRIVATE_MS_TO_NS(2); + } + assert(interval_ns == YASL_PRIVATE_MS_TO_NS(2)); + return 0; +} + /* 2. Repeating timer: ticks N times, then returns 0. */ static uint64_t repeat_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) { @@ -73,7 +96,7 @@ static uint64_t parent_timer_cb(void *ud, yasl_private_timer_id id, uint64_t int struct test_ctx *ctx = ud; assert(id == ctx->id); ctx->count += 1; - ctx->other_id = yasl_private_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx); + ctx->other_id = yasl_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx); assert(ctx->other_id > 0); return 0; } @@ -91,7 +114,7 @@ static uint64_t killer_timer_cb(void *ud, yasl_private_timer_id id, uint64_t int (void)id; (void)interval_ns; struct test_ctx *ctx = ud; ctx->count++; - assert(yasl_private_loop_del_timer(ctx->loop, ctx->other_id)); + assert(yasl_loop_del_timer(ctx->loop, ctx->other_id)); return 0; } @@ -111,90 +134,109 @@ static uint64_t ms_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interva static void test_loop_timers(void) { - struct yasl_private_loop *l = yasl_private_loop_create(); + struct yasl_loop *l = yasl_loop_create(); assert(l); /* Invalid arguments */ - assert(yasl_private_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0); - assert(yasl_private_loop_add_timer(l, 0, oneshot_cb, NULL) == 0); - assert(yasl_private_loop_add_timer(l, 1000, NULL, NULL) == 0); - assert(!yasl_private_loop_del_timer(NULL, 1)); - assert(!yasl_private_loop_del_timer(l, 0)); - assert(!yasl_private_loop_del_timer(l, 99999)); + assert(yasl_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0); + assert(yasl_loop_add_timer(l, 1000, NULL, NULL) == 0); + assert(!yasl_loop_del_timer(NULL, 1)); + assert(!yasl_loop_del_timer(l, 0)); + assert(!yasl_loop_del_timer(l, 99999)); assert(yasl_private_loop_next_timeout_ms(l) == -1); /* Test 1: Single-shot */ struct test_ctx ctx1 = { .loop = l }; - ctx1.id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1); + ctx1.id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1); assert(ctx1.id > 0); assert(yasl_private_loop_next_timeout_ms(l) >= 0); while (ctx1.count == 0) { - int handled = yasl_private_loop_step(l, 50); + int handled = yasl_loop_step(l, 50); assert(handled >= 0); } assert(ctx1.count == 1); /* Should be disarmed and removed */ - assert(!yasl_private_loop_del_timer(l, ctx1.id)); + assert(!yasl_loop_del_timer(l, ctx1.id)); assert(yasl_private_loop_next_timeout_ms(l) == -1); /* Test 2: Repeating */ struct test_ctx ctx2 = { .loop = l }; - ctx2.id = yasl_private_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2); + ctx2.id = yasl_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2); assert(ctx2.id > 0); while (!ctx2.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx2.count == 5); - assert(!yasl_private_loop_del_timer(l, ctx2.id)); + assert(!yasl_loop_del_timer(l, ctx2.id)); /* Test 3: Reschedule dynamically */ struct test_ctx ctx3 = { .loop = l }; - ctx3.id = yasl_private_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3); + ctx3.id = yasl_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3); assert(ctx3.id > 0); while (!ctx3.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx3.count == 2); /* Test 4: Timer cancellation before firing */ struct test_ctx ctx4 = { .loop = l }; - yasl_private_timer_id cancel_id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4); + yasl_private_timer_id cancel_id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4); assert(cancel_id > 0); - assert(yasl_private_loop_del_timer(l, cancel_id)); - assert(!yasl_private_loop_del_timer(l, cancel_id)); + assert(yasl_loop_del_timer(l, cancel_id)); + assert(!yasl_loop_del_timer(l, cancel_id)); /* Step for 10ms; timer should not fire */ - yasl_private_loop_step(l, 10); + yasl_loop_step(l, 10); assert(ctx4.count == 0); /* Test 5: Child timer added inside callback */ struct test_ctx ctx5 = { .loop = l }; - ctx5.id = yasl_private_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5); + ctx5.id = yasl_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5); while (ctx5.count < 11) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx5.count == 11); /* Test 6: Killer timer deletes victim timer before victim fires */ struct test_ctx ctx6 = { .loop = l }; - ctx6.id = yasl_private_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6); - ctx6.other_id = yasl_private_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6); + ctx6.id = yasl_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6); + ctx6.other_id = yasl_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6); while (ctx6.count == 0) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx6.count == 1); - yasl_private_loop_step(l, 20); + yasl_loop_step(l, 20); + + /* Test 8: Zero delay runs in the next step without waiting, and IDs are + * never handed out again. */ + int zero_runs = 0; + yasl_private_timer_id zero = yasl_loop_add_timer(l, 0, count_cb, &zero_runs); + assert(zero > 0 && yasl_private_loop_next_timeout_ms(l) == 0); + assert(yasl_loop_step(l, -1) == 1 && zero_runs == 1); + yasl_private_timer_id next = yasl_loop_add_timer(l, 0, count_cb, &zero_runs); + assert(next > zero); + assert(!yasl_loop_del_timer(l, zero) && yasl_loop_del_timer(l, next)); + + /* Test 9: A callback that grows the timer array still reschedules itself. */ + struct yasl_loop *grown = yasl_loop_create(); + assert(grown); + struct test_ctx ctx9 = { .loop = grown }; + ctx9.id = yasl_loop_add_timer(grown, 0, grow_cb, &ctx9); + while (ctx9.count < 2) + assert(yasl_loop_step(grown, 50) >= 0); + assert(!yasl_private_loop_timer_armed(grown, ctx9.id)); + assert(yasl_loop_destroy(grown) == 0); /* Test 7: Millisecond conversion */ struct test_ctx ctx7 = { .loop = l }; - ctx7.id = yasl_private_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), ms_timer_cb, &ctx7); + ctx7.id = yasl_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), ms_timer_cb, &ctx7); assert(ctx7.id > 0); while (!ctx7.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx7.count == 3); - yasl_private_loop_destroy(l); + yasl_loop_destroy(l); } /* Surface timer tests */ @@ -220,14 +262,14 @@ static uint64_t surface_suicide_cb(void *ud, yasl_private_timer_id id, uint64_t static void test_surface_timers(void) { - struct yasl_private_loop *loop = yasl_private_loop_create(); + struct yasl_loop *loop = yasl_loop_create(); assert(loop); struct yasl_private_display_spec spec = { .app_id = "org.yasl.timer_test" }; struct yasl_private_display *d = yasl_private_display_open(loop, &spec); if (!d) { /* Headless without display server */ - yasl_private_loop_destroy(loop); + yasl_loop_destroy(loop); return; } @@ -243,10 +285,10 @@ static void test_surface_timers(void) /* Application timers use the display's loop. */ struct test_ctx dctx = {0}; - dctx.id = yasl_private_loop_add_timer(yasl_private_display_loop(d), 2000000ull, oneshot_cb, &dctx); + dctx.id = yasl_loop_add_timer(yasl_private_display_loop(d), 2000000ull, oneshot_cb, &dctx); assert(dctx.id > 0); while (dctx.count == 0) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(dctx.count == 1); @@ -255,12 +297,12 @@ static void test_surface_timers(void) sctx.id = yasl_private_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx); assert(sctx.id > 0); while (sctx.count < 3) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(sctx.count >= 3); assert(yasl_private_surface_del_timer(s, sctx.id)); int count_before = sctx.count; - yasl_private_display_dispatch(d, 10); + yasl_loop_step(loop, 10); assert(sctx.count == count_before); /* A fired one-shot surface timer is retired, as a loop timer is. Many @@ -270,7 +312,7 @@ static void test_surface_timers(void) octx.id = yasl_private_surface_add_timer(s, 1000000ull, oneshot_cb, &octx); assert(octx.id > 0); while (octx.count == 0) - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); assert(!yasl_private_surface_del_timer(s, octx.id)); } @@ -280,7 +322,7 @@ static void test_surface_timers(void) assert(sctx2.id > 0); yasl_private_surface_destroy(s); /* Step display/loop; timer must not fire on dead surface */ - yasl_private_display_dispatch(d, 10); + yasl_loop_step(loop, 10); assert(sctx2.count == 0); /* Surface suicide from timer callback */ @@ -290,17 +332,19 @@ static void test_surface_timers(void) sctx3.id = yasl_private_surface_add_timer(s, 2000000ull, surface_suicide_cb, &sctx3); assert(sctx3.id > 0); while (sctx3.count == 0) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(sctx3.count == 1); assert(sctx3.surface == NULL); yasl_private_display_close(d); - yasl_private_loop_destroy(loop); + yasl_loop_destroy(loop); } int main(void) { + /* A wait that never ends fails here, not at the suite timeout. */ + alarm(10); test_loop_timers(); test_surface_timers(); printf("Timer tests passed successfully.\n"); |
