summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeandro Ribeiro <leandro.ribeiro@collabora.com>2024-05-17 19:11:11 -0300
committerPekka Paalanen <pq@iki.fi>2024-06-05 10:25:27 +0000
commit2ac57773164061128b2addfd2ae8e208c06d60a1 (patch)
treeeb5378c9d9aad50ad052d001aceb688b0f35bc03
parentbcd76c372b4241971488446be4197cecc9bc995d (diff)
clients: make main event loop more robust13.0.2
This makes the toytoolkit event loop more robust. Now it uses the canonical wl_display_prepare_read(), read() and cancel(). Also, it allows functions that run before the event loop to create Wayland queues and dispatch events related to such queue. Before our changes, this would cause issues, because of how the loop was written and also because dispatch_queue() reads the display fd and queue them on the appropriate event queues, it doesn't read only the events related to the queue we are interested. Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
-rw-r--r--clients/window.c59
1 files changed, 50 insertions, 9 deletions
diff --git a/clients/window.c b/clients/window.c
index 30f64109..64d7fe38 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -96,6 +96,7 @@ struct display {
uint32_t serial;
int display_fd;
+ bool display_fd_was_read;
uint32_t display_fd_events;
struct task display_task;
@@ -6620,7 +6621,8 @@ handle_display_data(struct task *task, uint32_t events)
}
if (events & EPOLLIN) {
- ret = wl_display_dispatch(display->display);
+ ret = wl_display_read_events(display->display);
+ display->display_fd_was_read = true;
if (ret == -1) {
display_exit(display);
return;
@@ -6872,6 +6874,19 @@ display_unwatch_fd(struct display *display, int fd)
epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
}
+static void
+run_deferred_tasks(struct display *display)
+{
+ struct task *task;
+
+ while (!wl_list_empty(&display->deferred_list)) {
+ task = container_of(display->deferred_list.prev,
+ struct task, link);
+ wl_list_remove(&task->link);
+ task->run(task, 0);
+ }
+}
+
void
display_run(struct display *display)
{
@@ -6881,17 +6896,39 @@ display_run(struct display *display)
display->running = 1;
while (1) {
- while (!wl_list_empty(&display->deferred_list)) {
- task = container_of(display->deferred_list.prev,
- struct task, link);
- wl_list_remove(&task->link);
- task->run(task, 0);
- }
+ /*
+ * Run the deferred tasks at least once. The loop below also run
+ * deferred tasks, but it will handle the deferred tasks created
+ * by the Wayland event handlers. With this call we make sure
+ * that deferred tasks will run even if there are no Wayland
+ * events to dispatch.
+ */
+ run_deferred_tasks(display);
- wl_display_dispatch_pending(display->display);
+ /*
+ * wl_display_prepare_read() fails until the default queue is
+ * empty. So we loop dispatching Wayland events and also running
+ * the deferred tasks that the Wayland event handlers may
+ * create. We do that until wl_display_prepare_read() succeeds.
+ * It is important to handle both the Wayland events and the
+ * deferred tasks before wl_display_prepare_read() succeeds, as
+ * the Wayland event handlers or the deferred tasks may lead to
+ * functions that try reading from the display fd (with
+ * wl_display_prepare_read() as well). That would cause a
+ * deadlock, because we only cancel/read at the end of this main
+ * event loop.
+ */
+ while (wl_display_prepare_read(display->display) == -1) {
+ ret = wl_display_dispatch_pending(display->display);
+ run_deferred_tasks(display);
+ if (ret == -1)
+ break;
+ }
- if (!display->running)
+ if (!display->running) {
+ wl_display_cancel_read(display->display);
break;
+ }
ret = wl_display_flush(display->display);
if (ret < 0 && errno == EAGAIN) {
@@ -6902,15 +6939,19 @@ display_run(struct display *display)
epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD,
display->display_fd, &ep[0]);
} else if (ret < 0) {
+ wl_display_cancel_read(display->display);
break;
}
count = epoll_wait(display->epoll_fd,
ep, ARRAY_LENGTH(ep), -1);
+ display->display_fd_was_read = false;
for (i = 0; i < count; i++) {
task = ep[i].data.ptr;
task->run(task, ep[i].events);
}
+ if (!display->display_fd_was_read)
+ wl_display_cancel_read(display->display);
}
}