diff options
author | Alexandros Frantzis <alexandros.frantzis@collabora.com> | 2022-11-15 10:50:53 +0200 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2023-02-28 11:22:04 +0000 |
commit | 0ba650202e742b23150054cf0740168cd529b010 (patch) | |
tree | 70934011a6f3912b9a16de0d9393c236ddb03313 | |
parent | d4d322885349c73f8753cca0c6d7754d2b0b411e (diff) | |
download | wayland-0ba650202e742b23150054cf0740168cd529b010.tar.gz |
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.
Signed-off-by: Alexandros Frantzis <alexandros.frantzis@collabora.com>
-rw-r--r-- | src/wayland-client.c | 16 | ||||
-rw-r--r-- | tests/queue-test.c | 93 |
2 files changed, 109 insertions, 0 deletions
diff --git a/src/wayland-client.c b/src/wayland-client.c index d3d7a7c..f7d7e68 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -301,6 +301,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 <stdlib.h> #include <stdint.h> #include <stdio.h> #include <stdbool.h> +#include <string.h> #include <unistd.h> +#include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> @@ -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); +} |