Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <campbell@blender.org>2022-06-10 11:24:26 +0300
committerCampbell Barton <campbell@blender.org>2022-06-10 11:42:34 +0300
commit28f852ccc396eff600467b150b279ada43c00d6b (patch)
tree42649982d4451526a0f27db4fe5f60e85dfabb6e
parent00aa57594c74c69d53ba326e48d2d1339ab99b75 (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.cpp90
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);
}
}