diff options
author | Leandro Ribeiro <leandro.ribeiro@collabora.com> | 2024-05-17 19:11:11 -0300 |
---|---|---|
committer | Pekka Paalanen <pq@iki.fi> | 2024-06-05 10:25:27 +0000 |
commit | 2ac57773164061128b2addfd2ae8e208c06d60a1 (patch) | |
tree | eb5378c9d9aad50ad052d001aceb688b0f35bc03 | |
parent | bcd76c372b4241971488446be4197cecc9bc995d (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.c | 59 |
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); } } |