From 9d5926ce12935e51de8c22a93f9f2b1cbfc6c6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 30 Oct 2019 20:03:11 +0100 Subject: [PATCH] term: add term_shutdown() This function unmaps the terminal window, removes itself from the wayland list of terminals and then finally destroys itself. We ensure we don't get any callbacks/events referring to a free:d terminal struct, we close all terminal related FDs and unmap the wayland window. Then, to be really really sure there aren't any (by the FDM) queued up events, we delay the self-destruction to the next FDM poll iteration, by opening an event FD and adding it to the FDM. The callback for the event FD removes the FD from the FDM again, and closes it. And then proceeds to destroy the terminal. --- terminal.c | 134 ++++++++++++++++++++++++++++++++++++++++------------- terminal.h | 1 + 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/terminal.c b/terminal.c index d889cfca..87adae62 100644 --- a/terminal.c +++ b/terminal.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -30,12 +31,8 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) { struct terminal *term = data; - if (events & EPOLLHUP) { - term->quit = true; - - if (!(events & EPOLLIN)) - return false; - } + if ((events & EPOLLHUP) && !(events & EPOLLIN)) + return term_shutdown(term); assert(events & EPOLLIN); @@ -101,7 +98,10 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) } } - return !(events & EPOLLHUP); + if (events & EPOLLHUP) + return term_shutdown(term); + + return true; } static bool @@ -494,23 +494,108 @@ close_fds: return NULL; } +static void +close_fd(struct terminal *term, int *fd) +{ + if (*fd == -1) + return; + + fdm_del(term->fdm, *fd); + close(*fd); + *fd = -1; +} + +static void +close_fds(struct terminal *term) +{ + close_fd(term, &term->delayed_render_timer.lower_fd); + close_fd(term, &term->delayed_render_timer.upper_fd); + close_fd(term, &term->blink.fd); + close_fd(term, &term->flash.fd); + close_fd(term, &term->ptmx); +} + +static bool +fdm_shutdown(struct fdm *fdm, int fd, int events, void *data) +{ + LOG_DBG("FDM shutdown"); + struct terminal *term = data; + + fdm_del(term->fdm, fd); + close(fd); + + /* + * Now there shouldn't be any more callbacks or events that can + * trigger, meaning it should be safe to destroy the terminal. + */ + + assert(term->wl->focused != term); + assert(term->wl->moused != term); + + tll_foreach(term->wl->terms, it) { + if (it->item == term) { + tll_remove(term->wl->terms, it); + break; + } + } + + term_destroy(term); + return true; +} + +bool +term_shutdown(struct terminal *term) +{ + /* Ensure we don't get any more events */ + close_fds(term); + + /* Destroy the wayland window, and consume the trail of Wayland events */ + wayl_win_destroy(term->window); + wl_display_roundtrip(term->wl->display); + term->window = NULL; + + /* + * At this point, we no longer receive any more events referring to us, and it _should_ be safe to destroy ourselves now. + * + * However, in the (unlikely) event that there are events already + * queued up by the FDM, we delay the self-destruction to the next + * FDM poll iteration. + * + * This is done by opening an event FD and adding it to the FDM. + */ + + int event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (event_fd == -1) { + LOG_ERRNO("failed to create terminal shutdown event FD"); + return false; + } + + if (!fdm_add(term->fdm, event_fd, EPOLLIN, &fdm_shutdown, term)) { + LOG_ERR("failed to add terminal shutdown event FD to the FDM"); + close(event_fd); + return false; + } + + if (write(event_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { + LOG_ERRNO("failed to send terminal shutdown event"); + fdm_del(term->fdm, event_fd); + close(event_fd); + return false; + } + + return true; +} + int term_destroy(struct terminal *term) { if (term == NULL) return 0; - wayl_win_destroy(term->window); + close_fds(term); - if (term->delayed_render_timer.lower_fd != -1) { - fdm_del(term->fdm, term->delayed_render_timer.lower_fd); - close(term->delayed_render_timer.lower_fd); - } - - if (term->delayed_render_timer.upper_fd != -1) { - fdm_del(term->fdm, term->delayed_render_timer.upper_fd); - close(term->delayed_render_timer.upper_fd); - } + if (term->window != NULL) + wayl_win_destroy(term->window); mtx_lock(&term->render.workers.lock); assert(tll_length(term->render.workers.queue) == 0); @@ -546,21 +631,6 @@ term_destroy(struct terminal *term) free(term->search.buf); - if (term->flash.fd != -1) { - fdm_del(term->fdm, term->flash.fd); - close(term->flash.fd); - } - - if (term->blink.fd != -1) { - fdm_del(term->fdm, term->blink.fd); - close(term->blink.fd); - } - - if (term->ptmx != -1) { - fdm_del(term->fdm, term->ptmx); - close(term->ptmx); - } - for (size_t i = 0; i < term->render.workers.count; i++) { if (term->render.workers.threads[i] != 0) thrd_join(term->render.workers.threads[i], NULL); diff --git a/terminal.h b/terminal.h index b16b9b51..5a0473b3 100644 --- a/terminal.h +++ b/terminal.h @@ -293,6 +293,7 @@ struct config; struct terminal *term_init( const struct config *conf, struct fdm *fdm, struct wayland *wayl, int argc, char *const *argv); +bool term_shutdown(struct terminal *term); int term_destroy(struct terminal *term); void term_reset(struct terminal *term, bool hard);