From 91f6d04564822d80b4cdccfd53b1abf02fd3cdea Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 15 Nov 2022 10:50:53 +0200 Subject: [PATCH] client: Warn when a queue is destroyed with attached proxies Log a warning if the queue is destroyed while proxies are still attached, to help developers debug and fix potential memory errors. Change-Id: I0bd8b036bb46f2346336b80cb450c4bda2f2bd53 Signed-off-by: Alexandros Frantzis --- src/wayland-client.c | 16 +++++++++ tests/queue-test.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/wayland-client.c b/src/wayland-client.c index 4ccc65d..3726742 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -336,6 +336,22 @@ wl_event_queue_release(struct wl_event_queue *queue) { struct wl_closure *closure; + if (!wl_list_empty(&queue->proxy_list)) { + struct wl_proxy *proxy, *tmp; + + wl_log("warning: queue %p destroyed while proxies still " + "attached:\n", queue); + + wl_list_for_each_safe(proxy, tmp, &queue->proxy_list, + queue_link) { + wl_log(" %s@%u still attached\n", + proxy->object.interface->name, + proxy->object.id); + wl_list_remove(&proxy->queue_link); + wl_list_init(&proxy->queue_link); + } + } + while (!wl_list_empty(&queue->event_list)) { closure = wl_container_of(queue->event_list.next, closure, link); diff --git a/tests/queue-test.c b/tests/queue-test.c index 86a3602..f9254f7 100644 --- a/tests/queue-test.c +++ b/tests/queue-test.c @@ -23,11 +23,14 @@ * SOFTWARE. */ +#define _GNU_SOURCE /* For memrchr */ #include #include #include #include +#include #include +#include #include #include #include @@ -303,6 +306,84 @@ client_test_queue_set_queue_race(void) wl_display_disconnect(display); } +static char * +map_file(int fd, size_t *len) +{ + char *data; + + *len = lseek(fd, 0, SEEK_END); + data = mmap(0, *len, PROT_READ, MAP_PRIVATE, fd, 0); + assert(data != MAP_FAILED && "Failed to mmap file"); + + return data; +} + +static char * +last_line_of(char *s) +{ + size_t len = strlen(s); + char *last; + + last = memrchr(s, '\n', len); + /* If we found a newline at end of string, find the previous one. */ + if (last && last[1] == 0) + last = memrchr(s, '\n', len - 1); + /* If we have a newline, the last line starts after the newline. + * Otherwise, the whole string is the last line. */ + if (last) + last += 1; + else + last = s; + + return last; +} + +static void +client_test_queue_destroy_with_attached_proxies(void) +{ + struct wl_event_queue *queue; + struct wl_display *display; + struct wl_display *display_wrapper; + struct wl_callback *callback; + char *log; + size_t log_len; + char callback_name[24]; + int ret; + + display = wl_display_connect(NULL); + assert(display); + + /* Pretend we are in a separate thread where a thread-local queue is + * used. */ + queue = wl_display_create_queue(display); + assert(queue); + + /* Create a sync dispatching events on the thread-local queue. */ + display_wrapper = wl_proxy_create_wrapper(display); + assert(display_wrapper); + wl_proxy_set_queue((struct wl_proxy *) display_wrapper, queue); + callback = wl_display_sync(display_wrapper); + wl_proxy_wrapper_destroy(display_wrapper); + assert(callback != NULL); + + /* Destroy the queue before the attached object. */ + wl_event_queue_destroy(queue); + + /* Check that the log contains some information about the attached + * wl_callback proxy. */ + log = map_file(client_log_fd, &log_len); + ret = snprintf(callback_name, sizeof(callback_name), "wl_callback@%u", + wl_proxy_get_id((struct wl_proxy *) callback)); + assert(ret > 0 && ret < (int)sizeof(callback_name) && + "callback name creation failed (possibly truncated)"); + assert(strstr(last_line_of(log), callback_name)); + munmap(log, log_len); + + wl_callback_destroy(callback); + + wl_display_disconnect(display); +} + static void dummy_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -382,3 +463,15 @@ TEST(queue_set_queue_race) display_destroy(d); } + +TEST(queue_destroy_with_attached_proxies) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_destroy_with_attached_proxies); + display_run(d); + + display_destroy(d); +} -- 2.7.4