mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-11-04 20:55:58 +01:00
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).
This commit is contained in:
parent
6c277e3c39
commit
270b8dd342
1 changed files with 52 additions and 31 deletions
|
@ -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 (sigaction(SIGBUS, &data.prev_action, NULL) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "sigaction failed");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
sigbus_data = NULL;
|
||||
|
||||
mapping_consider_destroy(data.mapping);
|
||||
if (sigbus_data == NULL) {
|
||||
if (sigaction(SIGBUS, &buffer->sigbus_data.prev_action, NULL) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "sigaction failed");
|
||||
}
|
||||
}
|
||||
|
||||
mapping_consider_destroy(buffer->sigbus_data.mapping);
|
||||
}
|
||||
|
||||
static const struct wlr_buffer_impl buffer_impl = {
|
||||
|
|
Loading…
Reference in a new issue