summaryrefslogtreecommitdiff
path: root/tests/common/test_loop.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-16 09:33:39 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-16 09:33:39 +0300
commita075a2941f8fcdae155495da634cf63368c950b7 (patch)
treea33b5636ef7b9e4879cb8ab52220e805a5e30348 /tests/common/test_loop.c
parentc2832a63547e6328afde25bb3b8145267eadef47 (diff)
Pace frame requests by the frame presented after them
A frame request now only records intent. Each loop step resolves it after the callbacks that present, so a request made before the present waits for that frame. An animation that asked first got two REFRESH events per frame on X11 and with GL. With no frame in flight, the REFRESH comes one refresh interval after the last present or answer, so a request without a present cannot spin. Queued events now go before an owed REFRESH. Input reaches the application before it draws, and a queued REFRESH pays an owed one for the same surface. The loop prepare only peeks at a lost event, so deliver can report it as a final loss. A failed push from another thread wakes the loop. Signals and posts run at the end of a step, after descriptors and timers. A signal handler now sees the output that came with it. A render-thread GL swap counts as in flight on Wayland. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/common/test_loop.c')
-rw-r--r--tests/common/test_loop.c45
1 files changed, 44 insertions, 1 deletions
diff --git a/tests/common/test_loop.c b/tests/common/test_loop.c
index bdc33b7..a77b6a5 100644
--- a/tests/common/test_loop.c
+++ b/tests/common/test_loop.c
@@ -237,6 +237,47 @@ static void test_signals(void)
sigaction(SIGUSR2, &dfl, NULL);
}
+struct order_log {
+ char order[8];
+ int count;
+};
+
+static void order_signal(void *ud, int signo)
+{
+ (void)signo;
+ struct order_log *log = ud;
+ log->order[log->count++] = 's';
+}
+
+static void order_fd(void *ud, int fd, short revents)
+{
+ (void)revents;
+ struct order_log *log = ud;
+ char b;
+ assert(read(fd, &b, 1) == 1);
+ log->order[log->count++] = 'f';
+}
+
+/* A signal runs after the descriptors of its step, so a child's last output
+ * is read before its exit is handled. */
+static void test_signal_order(void)
+{
+ yasl_loop *l = yasl_loop_create();
+ assert(l);
+ struct order_log log = {0};
+ assert(yasl_loop_watch_signals(l, (const int[]){SIGUSR1}, 1, order_signal, &log) == 0);
+ int p[2];
+ assert(pipe(p) == 0);
+ assert(yasl_loop_add_fd(l, p[0], POLLIN, order_fd, &log) > 0);
+ assert(write(p[1], "x", 1) == 1);
+ raise(SIGUSR1);
+ assert(yasl_loop_step(l, 0) >= 1);
+ assert(log.count == 2 && memcmp(log.order, "fs", 2) == 0);
+ close(p[0]);
+ close(p[1]);
+ assert(yasl_loop_destroy(l) == 0);
+}
+
struct fd_log {
int calls;
short revents;
@@ -364,7 +405,8 @@ static void test_interrupted_wait(void)
assert(yasl_loop_step(l, 100) == 0);
uint64_t elapsed_ms = (yasl_time_ns() - start) / 1000000u;
pthread_join(t, NULL);
- assert(elapsed_ms >= 90 && elapsed_ms < 140);
+ /* Lateness under load is not the bug; an early return is. */
+ assert(elapsed_ms >= 90 && elapsed_ms < 1000);
assert(yasl_loop_destroy(l) == 0);
sigaction(SIGUSR2, &old, NULL);
}
@@ -376,6 +418,7 @@ int main(void)
test_post_order();
test_cross_thread();
test_signals();
+ test_signal_order();
test_fds();
test_reentry_and_quit();
test_interrupted_wait();