diff options
author | Campbell Barton <campbell@blender.org> | 2022-06-10 11:24:26 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-06-10 11:42:34 +0300 |
commit | 28f852ccc396eff600467b150b279ada43c00d6b (patch) | |
tree | 42649982d4451526a0f27db4fe5f60e85dfabb6e | |
parent | 00aa57594c74c69d53ba326e48d2d1339ab99b75 (diff) |
Fix T98758: Crash setting the clipboard in Wayland
Accessing the clipboard in wayland wasn't thread safe,
use locks for the clipboard, drag & drop data access and when
setting the systems clipboard.
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWayland.cpp | 90 |
1 files changed, 65 insertions, 25 deletions
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index b9696ab6354..c2240e34890 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -37,6 +37,7 @@ #include <unistd.h> #include <cstring> +#include <mutex> static GHOST_IWindow *get_window(struct wl_surface *surface); @@ -91,8 +92,6 @@ struct data_offer_t { struct data_source_t { struct wl_data_source *data_source; - /** Last device that was active. */ - uint32_t source_serial; char *buffer_out; }; @@ -138,10 +137,17 @@ struct input_t { struct wl_data_device *data_device = nullptr; /** Drag & Drop. */ struct data_offer_t *data_offer_dnd; + std::mutex data_offer_dnd_lock; + /** Copy & Paste. */ struct data_offer_t *data_offer_copy_paste; + std::mutex data_offer_copy_paste_mutex; struct data_source_t *data_source; + std::mutex data_source_mutex; + + /** Last device that was active. */ + uint32_t data_source_serial; }; struct display_t { @@ -174,6 +180,9 @@ struct display_t { static GHOST_WindowManager *window_manager = nullptr; +/** Check this lock before accessing `GHOST_SystemWayland::selection` from a thread. */ +static std::mutex system_selection_mutex; + /** * Callback for WAYLAND to run when there is an error. * @@ -509,6 +518,7 @@ static const zwp_relative_pointer_v1_listener relative_pointer_listener = { static void dnd_events(const input_t *const input, const GHOST_TEventType event) { + /* NOTE: `input->data_offer_dnd_lock` must already be locked. */ const uint64_t time = input->system->getMilliSeconds(); GHOST_IWindow *const window = static_cast<GHOST_WindowWayland *>( wl_surface_get_user_data(input->focus_dnd)); @@ -523,7 +533,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event) } } -static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) +static std::string read_pipe(data_offer_t *data_offer, + const std::string mime_receive, + std::mutex *mutex) { int pipefd[2]; if (pipe(pipefd) != 0) { @@ -532,6 +544,13 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); close(pipefd[1]); + data_offer->in_use.store(false); + + if (mutex) { + mutex->unlock(); + } + /* WARNING: `data_offer` may be freed from now on. */ + std::string data; ssize_t len; char buffer[4096]; @@ -539,7 +558,6 @@ static std::string read_pipe(data_offer_t *data_offer, const std::string mime_re data.insert(data.end(), buffer, buffer + len); } close(pipefd[0]); - data_offer->in_use.store(false); return data; } @@ -562,7 +580,10 @@ static void data_source_send(void *data, const char * /*mime_type*/, int32_t fd) { - const char *const buffer = static_cast<char *>(data); + input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_source_mutex}; + + const char *const buffer = input->data_source->buffer_out; if (write(fd, buffer, strlen(buffer)) < 0) { GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl); } @@ -679,6 +700,8 @@ static void data_device_enter(void *data, struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_lock}; + input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); data_offer_t *data_offer = input->data_offer_dnd; @@ -702,6 +725,7 @@ static void data_device_enter(void *data, static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_lock}; dnd_events(input, GHOST_kEventDraggingExited); input->focus_dnd = nullptr; @@ -720,6 +744,8 @@ static void data_device_motion(void *data, wl_fixed_t y) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_lock}; + input->data_offer_dnd->dnd.x = wl_fixed_to_int(x); input->data_offer_dnd->dnd.y = wl_fixed_to_int(y); dnd_events(input, GHOST_kEventDraggingUpdated); @@ -728,6 +754,8 @@ static void data_device_motion(void *data, static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/) { input_t *input = static_cast<input_t *>(data); + std::lock_guard lock{input->data_offer_dnd_lock}; + data_offer_t *data_offer = input->data_offer_dnd; const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), @@ -742,7 +770,7 @@ static void data_device_drop(void *data, struct wl_data_device * /*wl_data_devic const int x = data_offer->dnd.x; const int y = data_offer->dnd.y; - const std::string data = read_pipe(data_offer, mime_receive); + const std::string data = read_pipe(data_offer, mime_receive, nullptr); wl_data_offer_finish(data_offer->id); wl_data_offer_destroy(data_offer->id); @@ -808,6 +836,9 @@ static void data_device_selection(void *data, struct wl_data_offer *id) { input_t *input = static_cast<input_t *>(data); + + std::lock_guard lock{input->data_offer_copy_paste_mutex}; + data_offer_t *data_offer = input->data_offer_copy_paste; /* Delete old data offer. */ @@ -825,22 +856,28 @@ static void data_device_selection(void *data, data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); input->data_offer_copy_paste = data_offer; - std::string mime_receive; - for (const std::string type : {mime_text_utf8, mime_text_plain}) { - if (data_offer->types.count(type)) { - mime_receive = type; - break; + auto read_selection = [](input_t *input) { + GHOST_SystemWayland *const system = input->system; + input->data_offer_copy_paste_mutex.lock(); + + data_offer_t *data_offer = input->data_offer_copy_paste; + std::string mime_receive; + for (const std::string type : {mime_text_utf8, mime_text_plain}) { + if (data_offer->types.count(type)) { + mime_receive = type; + break; + } } - } + const std::string data = read_pipe( + data_offer, mime_receive, &input->data_offer_copy_paste_mutex); - auto read_selection = [](GHOST_SystemWayland *const system, - data_offer_t *data_offer, - const std::string mime_receive) { - const std::string data = read_pipe(data_offer, mime_receive); - system->setSelection(data); + { + std::lock_guard lock{system_selection_mutex}; + system->setSelection(data); + } }; - std::thread read_thread(read_selection, input->system, data_offer, mime_receive); + std::thread read_thread(read_selection, input); read_thread.detach(); } @@ -1066,7 +1103,7 @@ static void pointer_button(void *data, break; } - input->data_source->source_serial = serial; + input->data_source_serial = serial; input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); input->system->pushEvent(new GHOST_EventButton( input->system->getMilliSeconds(), etype, win, ebutton, GHOST_TABLET_DATA_NONE)); @@ -1241,7 +1278,7 @@ static void keyboard_key(void *data, key_data.utf8_buf[0] = '\0'; } - input->data_source->source_serial = serial; + input->data_source_serial = serial; GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>( wl_surface_get_user_data(input->focus_keyboard)); @@ -1642,7 +1679,11 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c return; } - data_source_t *data_source = d->inputs[0]->data_source; + input_t *input = d->inputs[0]; + + std::lock_guard lock{input->data_source_mutex}; + + data_source_t *data_source = input->data_source; /* Copy buffer. */ free(data_source->buffer_out); @@ -1652,16 +1693,15 @@ void GHOST_SystemWayland::putClipboard(const char *buffer, bool /*selection*/) c data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager); - wl_data_source_add_listener( - data_source->data_source, &data_source_listener, data_source->buffer_out); + wl_data_source_add_listener(data_source->data_source, &data_source_listener, input); for (const std::string &type : mime_send) { wl_data_source_offer(data_source->data_source, type.c_str()); } - if (!d->inputs.empty() && d->inputs[0]->data_device) { + if (input->data_device) { wl_data_device_set_selection( - d->inputs[0]->data_device, data_source->data_source, data_source->source_serial); + input->data_device, data_source->data_source, input->data_source_serial); } } |