diff options
author | Simon Ser <contact@emersion.fr> | 2022-09-23 13:47:52 +0200 |
---|---|---|
committer | Simon Zeni <simon@bl4ckb0ne.ca> | 2022-11-15 16:30:00 +0000 |
commit | 270b8dd342ca72c6bac7c6a908b3dbf615116dd8 (patch) | |
tree | 8b2c1bd791d3e4a54f0053f99b203eae1373b9b9 | |
parent | 6c277e3c395996db690dd80dd79f651c3baec883 (diff) |
shm: allow accessing multiple shm mapping concurrently
Use a basic linked list to store the currently active mappings.
Note that we don't actually need to implement a full lock-free
atomic linked list here. The signal handler will never write to
the list, it will only read it. Only the main thread will write.
We need to always expose a consistent view of the list to the
signal handler (the main thread might be interrupted at any point
by the signal handler).
-rw-r--r-- | types/wlr_shm.c | 81 |
1 files changed, 51 insertions, 30 deletions
diff --git a/types/wlr_shm.c b/types/wlr_shm.c index f8fdbad2..492557f3 100644 --- a/types/wlr_shm.c +++ b/types/wlr_shm.c @@ -55,6 +55,7 @@ struct wlr_shm_mapping { struct wlr_shm_sigbus_data { struct wlr_shm_mapping *mapping; struct sigaction prev_action; + struct wlr_shm_sigbus_data *_Atomic next; }; struct wlr_shm_buffer { @@ -122,13 +123,14 @@ static struct wlr_shm_mapping *mapping_create(int fd, size_t size) { } static void mapping_consider_destroy(struct wlr_shm_mapping *mapping) { - struct wlr_shm_mapping *mapping_in_use = NULL; - if (sigbus_data != NULL) { - mapping_in_use = sigbus_data->mapping; + if (!mapping->dropped) { + return; } - if (mapping_in_use == mapping || !mapping->dropped) { - return; + for (struct wlr_shm_sigbus_data *cur = sigbus_data; cur != NULL; cur = cur->next) { + if (cur->mapping == mapping) { + return; + } } munmap(mapping->data, mapping->size); @@ -181,13 +183,22 @@ static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, } static void handle_sigbus(int sig, siginfo_t *info, void *context) { - struct wlr_shm_sigbus_data data = *sigbus_data; - struct wlr_shm_mapping *mapping = data.mapping; + assert(sigbus_data != NULL); + struct sigaction prev_action = sigbus_data->prev_action; + // Check whether the offending address is inside of the wl_shm_pool's mapped + // space uintptr_t addr = (uintptr_t)info->si_addr; - uintptr_t mapping_start = (uintptr_t)mapping->data; - if (addr < mapping_start || addr >= mapping_start + mapping->size) { - // The offending address is outside of the wl_shm_pool's mapped space + struct wlr_shm_mapping *mapping = NULL; + for (struct wlr_shm_sigbus_data *data = sigbus_data; data != NULL; data = data->next) { + uintptr_t mapping_start = (uintptr_t)data->mapping->data; + size_t mapping_size = data->mapping->size; + if (addr >= mapping_start && addr < mapping_start + mapping_size) { + mapping = data->mapping; + break; + } + } + if (mapping == NULL) { goto reraise; } @@ -202,10 +213,10 @@ static void handle_sigbus(int sig, siginfo_t *info, void *context) { return; reraise: - if (data.prev_action.sa_flags & SA_SIGINFO) { - data.prev_action.sa_sigaction(sig, info, context); + if (prev_action.sa_flags & SA_SIGINFO) { + prev_action.sa_sigaction(sig, info, context); } else { - data.prev_action.sa_handler(sig); + prev_action.sa_handler(sig); } } @@ -218,21 +229,20 @@ static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, return false; } - if (sigbus_data != NULL) { - wlr_log(WLR_ERROR, "Cannot concurrently access data of two wlr_shm_buffers"); - return false; - } - // Install a SIGBUS handler. SIGBUS is triggered if the client shrinks the // backing file, and then we try to access the mapping. - struct sigaction new_action = { - .sa_sigaction = handle_sigbus, - .sa_flags = SA_SIGINFO | SA_NODEFER, - }; struct sigaction prev_action; - if (sigaction(SIGBUS, &new_action, &prev_action) != 0) { - wlr_log_errno(WLR_ERROR, "sigaction failed"); - return false; + if (sigbus_data == NULL) { + struct sigaction new_action = { + .sa_sigaction = handle_sigbus, + .sa_flags = SA_SIGINFO | SA_NODEFER, + }; + if (sigaction(SIGBUS, &new_action, &prev_action) != 0) { + wlr_log_errno(WLR_ERROR, "sigaction failed"); + return false; + } + } else { + prev_action = sigbus_data->prev_action; } struct wlr_shm_mapping *mapping = buffer->pool->mapping; @@ -240,6 +250,7 @@ static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, buffer->sigbus_data = (struct wlr_shm_sigbus_data){ .mapping = mapping, .prev_action = prev_action, + .next = sigbus_data, }; sigbus_data = &buffer->sigbus_data; @@ -252,14 +263,24 @@ static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, static void buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); - struct wlr_shm_sigbus_data data = *sigbus_data; + if (sigbus_data == &buffer->sigbus_data) { + sigbus_data = buffer->sigbus_data.next; + } else { + for (struct wlr_shm_sigbus_data *cur = sigbus_data; cur != NULL; cur = cur->next) { + if (cur->next == &buffer->sigbus_data) { + cur->next = buffer->sigbus_data.next; + break; + } + } + } - if (sigaction(SIGBUS, &data.prev_action, NULL) != 0) { - wlr_log_errno(WLR_ERROR, "sigaction failed"); + if (sigbus_data == NULL) { + if (sigaction(SIGBUS, &buffer->sigbus_data.prev_action, NULL) != 0) { + wlr_log_errno(WLR_ERROR, "sigaction failed"); + } } - sigbus_data = NULL; - mapping_consider_destroy(data.mapping); + mapping_consider_destroy(buffer->sigbus_data.mapping); } static const struct wlr_buffer_impl buffer_impl = { |