qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] qemu-ui-gtk clipboard: fix for freeze-crashes v2


From: Edmund Raile
Subject: [PATCH] qemu-ui-gtk clipboard: fix for freeze-crashes v2
Date: Sat, 14 Oct 2023 08:48:50 +0000

summary

adresses https://gitlab.com/qemu-project/qemu/-/issues/1150
replaces blocking gtk_clipboard_wait_is_text_available in gd_owner_change and 
gtk_clipboard_wait_for_text in gd_clipboard_request with asynchronous 
gtk_clipboard_request_text
uses serial_last and serial of QemuClipboardInfo to only process new clipboard 
data



In response to [gemu-gtk-clipboard freezing and crashing 
guests](https://gitlab.com/qemu-project/qemu/-/issues/1150).

I think I might have a solution for the gtk clipboard sometimes crashing guests.

@kolAflash I couldn't have done it without you, figuring out 
`gtk_clipboard_wait_is_text_available(clipboard)` was the issue is half the 
work.

The real issue is that it's blocking and I'd wager that's a big no-no since 
qemu & KVM have to run the VM + OS, preferably as real-time as possible.
Something times out and you get a core dump.

As a replacement, `gtk_clipboard_request_text`, which is async and non-blocking 
is a better choice, hopefully.
It requires an additional function to handle receiving text.

In a previous (first) attempt of submitting a patch 
(https://lists.gnu.org/archive/html/qemu-devel/2023-09/msg06027.html), Mr. 
Lureau gave advice which I followed here:
 * now both gd_owner_change and gd_clipboard_request are asynchronous by means 
of the gd_clipboard_transfer_text_to_guest_callback
 * the QemuClipboardInfo struct now carries a serial_last field which is used 
by gd_clipboard_transfer_text_to_guest_callback to only process the clipboard 
when new

I hope the comments are acceptable, they should be very useful to people not 
particularly familiar with he workings of both qemu and gtk clipboards (like 
me).

To not risk breaking anything in the mailing list, I'm starting this new mail 
thread instead of replying to my first one.
Hopefully I'll get it right this time.

Kind regards,
Edmund Raile

Signed-off-by: Edmund Raile <edmund.raile@proton.me>



---
 include/ui/clipboard.h |  2 ++
 ui/gtk-clipboard.c     | 34 ++++++++++++++++++++--------------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h
index ab6acdbd8a..123c04fc07 100644
--- a/include/ui/clipboard.h
+++ b/include/ui/clipboard.h
@@ -106,6 +106,7 @@ struct QemuClipboardNotify {
  * @types: clipboard data array (one entry per type).
  * @has_serial: whether @serial is available.
  * @serial: the grab serial counter.
+ * @serial_last: used by GTK UI to discard outdated transaction results.
  *
  * Clipboard content data and metadata.
  */
@@ -115,6 +116,7 @@ struct QemuClipboardInfo {
     QemuClipboardSelection selection;
     bool has_serial;
     uint32_t serial;
+    uint32_t serial_last;
     struct {
         bool available;
         bool requested;
diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c
index 8d8a636fd1..9e96cc2fb5 100644
--- a/ui/gtk-clipboard.c
+++ b/ui/gtk-clipboard.c
@@ -133,26 +133,38 @@ static void gd_clipboard_notify(Notifier *notifier, void 
*data)
     }
 }
 
+/* asynchronous clipboard text transfer (host -> guest): callback */
+static void gd_clipboard_transfer_text_to_guest_callback(GtkClipboard 
*clipboard, const gchar *text, gpointer data)
+{
+    QemuClipboardInfo *info = (QemuClipboardInfo *)data;
+
+    // serial_last is intentionally not stored as a static in this function as 
callbacks implementing other data types (e.g. images) need access as well
+
+    if (text && (info->serial > info->serial_last)) {
+        info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
+        qemu_clipboard_update(info);
+        info->serial_last = info->serial;
+    }
+
+    qemu_clipboard_info_unref(info);
+}
+
+/* asynchronous clipboard transfer (host -> guest) initiator when guest 
requests clipboard data */
 static void gd_clipboard_request(QemuClipboardInfo *info,
                                  QemuClipboardType type)
 {
     GtkDisplayState *gd = container_of(info->owner, GtkDisplayState, cbpeer);
-    char *text;
 
     switch (type) {
     case QEMU_CLIPBOARD_TYPE_TEXT:
-        text = gtk_clipboard_wait_for_text(gd->gtkcb[info->selection]);
-        if (text) {
-            qemu_clipboard_set_data(&gd->cbpeer, info, type,
-                                    strlen(text), text, true);
-            g_free(text);
-        }
+        gtk_clipboard_request_text(gd->gtkcb[info->selection], 
gd_clipboard_transfer_text_to_guest_callback, info);
         break;
     default:
         break;
     }
 }
 
+/* asynchronous clipboard transfer (host -> guest) initiator when host has new 
clipboard data */
 static void gd_owner_change(GtkClipboard *clipboard,
                             GdkEvent *event,
                             gpointer data)
@@ -166,16 +178,10 @@ static void gd_owner_change(GtkClipboard *clipboard,
         return;
     }
 
-
     switch (event->owner_change.reason) {
     case GDK_OWNER_CHANGE_NEW_OWNER:
         info = qemu_clipboard_info_new(&gd->cbpeer, s);
-        if (gtk_clipboard_wait_is_text_available(clipboard)) {
-            info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
-        }
-
-        qemu_clipboard_update(info);
-        qemu_clipboard_info_unref(info);
+        gtk_clipboard_request_text(clipboard, 
gd_clipboard_transfer_text_to_guest_callback, info);
         break;
     default:
         qemu_clipboard_peer_release(&gd->cbpeer, s);
-- 
2.42.0





reply via email to

[Prev in Thread] Current Thread [Next in Thread]