[gnome-remote-desktop] clipboard-rdp: Don't wait for FormatListResponses



commit 74a5d9116f3b6e1825444511bf7b0099c1925c32
Author: Pascal Nowack <Pascal Nowack gmx de>
Date:   Thu Feb 25 14:51:55 2021 +0100

    clipboard-rdp: Don't wait for FormatListResponses
    
    Currently, when gnome-remote-desktop sends a new FormatList to the RDP
    client, it will wait for the RDP client to send the FormatListResponse
    for the previous FormatList, when a response is pending.
    However, if the user on the server side decides to hold CTRL+C to spam
    new FormatLists and the clipboard handling on the client sides goes up
    in flames, gnome-remote-desktop won't get a response any more.
    Assuming the number of FormatLists to send is 1000, then
    gnome-remote-desktop will block the main thread in total for 4000
    seconds, which is obviously an atrocious situation.
    
    Additionally, mutter has three types of clipboards:
    The normal one, one for primary selection and one for Drag'n'Drop
    usage.
    For gnome-remote-desktop only the first one is relevant.
    In addition to that for each of these three clipboards there can only
    be one clipboard owner.
    
    This characteristic allows gnome-remote-desktop to optimize this
    situation:
    Whenever, a FormatList is about to be sent, but the previous
    FormatListResponse is still pending, queue the new FormatList.
    If there is already a FormatList queued, replace it.
    To make this feasible, the handling of FormatLists must be revamped to
    work in an asynchronous way, as the dbus handling happens in the main
    thread and the pending dbus messages are handled inside the dbus
    component in gnome-remote-desktop.
    Whenever a new FormatList is queued (due to pending
    FormatListResponse), create a timeout-source which clears the pending
    FormatList upon reaching timeout.
    When a FormatListResponse is received, this timeout will be removed.
    
    With this handling, gnome-remote-desktop won't spam further the RDP
    client, when it doesn't get a response any more.
    It also reduces bandwidth usage and allows the client side to restart
    the clipboard handling.
    With mstsc, for example, the clipboard handling is done in rdpclip.exe.
    The handling of rdpclip.exe is relatively stable and it won't go up in
    flames, unless clipboard locking is supported.
    With this commit, however, rdpclip.exe won't even do that any more,
    since a lot of FormatLists are already sorted out, when FormatLists are
    spammed.

 src/grd-clipboard-rdp.c | 114 ++++++++++++++++++++++++++++--------------------
 1 file changed, 67 insertions(+), 47 deletions(-)
---
diff --git a/src/grd-clipboard-rdp.c b/src/grd-clipboard-rdp.c
index 24b3942..d2edaf5 100644
--- a/src/grd-clipboard-rdp.c
+++ b/src/grd-clipboard-rdp.c
@@ -64,6 +64,7 @@ struct _GrdClipboardRdp
 
   GHashTable *allowed_server_formats;
   GList *pending_server_formats;
+  GList *queued_server_formats;
   gboolean server_file_contents_requests_allowed;
   uint16_t format_list_response_msg_flags;
 
@@ -79,6 +80,7 @@ struct _GrdClipboardRdp
   HANDLE completed_format_data_request_event;
   HANDLE format_list_received_event;
   HANDLE format_list_response_received_event;
+  unsigned int pending_server_formats_drop_id;
   unsigned int server_format_list_update_id;
   unsigned int server_format_data_request_id;
   unsigned int client_format_list_response_id;
@@ -172,10 +174,9 @@ remove_duplicated_clipboard_mime_types (GrdClipboardRdp  *clipboard_rdp,
 }
 
 static void
-grd_clipboard_rdp_update_client_mime_type_list (GrdClipboard *clipboard,
-                                                GList        *mime_type_list)
+send_mime_type_list (GrdClipboardRdp *clipboard_rdp,
+                     GList           *mime_type_list)
 {
-  GrdClipboardRdp *clipboard_rdp = GRD_CLIPBOARD_RDP (clipboard);
   GrdRdpFuseClipboard *rdp_fuse_clipboard = clipboard_rdp->rdp_fuse_clipboard;
   CliprdrServerContext *cliprdr_context = clipboard_rdp->cliprdr_context;
   CLIPRDR_FORMAT_LIST format_list = {0};
@@ -185,50 +186,7 @@ grd_clipboard_rdp_update_client_mime_type_list (GrdClipboard *clipboard,
   uint32_t i;
   GList *l;
 
-  if (clipboard_rdp->pending_server_formats)
-    {
-      HANDLE format_list_response_received_event;
-      HANDLE events[2];
-
-      format_list_response_received_event =
-        clipboard_rdp->format_list_response_received_event;
-
-      events[0] = clipboard_rdp->stop_event;
-      events[1] = format_list_response_received_event;
-      WaitForMultipleObjects (2, events, FALSE, MAX_WAIT_TIME);
-
-      if (WaitForSingleObject (clipboard_rdp->stop_event, 0) == WAIT_OBJECT_0)
-        {
-          g_list_free (mime_type_list);
-          return;
-        }
-
-      if (WaitForSingleObject (format_list_response_received_event, 0) == WAIT_OBJECT_0)
-        {
-          update_allowed_server_formats (clipboard_rdp, TRUE);
-
-          g_clear_handle_id (&clipboard_rdp->client_format_list_response_id,
-                             g_source_remove);
-          ResetEvent (format_list_response_received_event);
-        }
-      else
-        {
-          g_warning ("[RDP.CLIPRDR] Possible protocol violation: Client did not "
-                     "send format list response (Timeout reached)");
-
-          update_allowed_server_formats (clipboard_rdp, FALSE);
-        }
-    }
-
-  remove_duplicated_clipboard_mime_types (clipboard_rdp, &mime_type_list);
-
   n_formats = g_list_length (mime_type_list);
-  if (!n_formats)
-    {
-      g_list_free (mime_type_list);
-      return;
-    }
-
   cliprdr_formats = g_malloc0 (n_formats * sizeof (CLIPRDR_FORMAT));
   for (i = 0, l = mime_type_list; i < n_formats; ++i, l = l->next)
     {
@@ -292,12 +250,63 @@ grd_clipboard_rdp_update_client_mime_type_list (GrdClipboard *clipboard,
   format_list.formats = cliprdr_formats;
   format_list.numFormats = n_formats;
 
+  g_debug ("[RDP.CLIPRDR] Sending FormatList");
   cliprdr_context->ServerFormatList (cliprdr_context, &format_list);
 
   g_free (cliprdr_formats);
   g_list_free (mime_type_list);
 }
 
+static gboolean
+drop_pending_server_formats (gpointer user_data)
+{
+  GrdClipboardRdp *clipboard_rdp = user_data;
+  GList *queued_server_formats;
+
+  g_warning ("[RDP.CLIPRDR] Possible protocol violation: Client did not send "
+             "format list response (Timeout reached)");
+  update_allowed_server_formats (clipboard_rdp, FALSE);
+
+  queued_server_formats = g_steal_pointer (&clipboard_rdp->queued_server_formats);
+  if (queued_server_formats)
+    send_mime_type_list (clipboard_rdp, queued_server_formats);
+
+  clipboard_rdp->pending_server_formats_drop_id = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+grd_clipboard_rdp_update_client_mime_type_list (GrdClipboard *clipboard,
+                                                GList        *mime_type_list)
+{
+  GrdClipboardRdp *clipboard_rdp = GRD_CLIPBOARD_RDP (clipboard);
+
+  remove_duplicated_clipboard_mime_types (clipboard_rdp, &mime_type_list);
+  if (!mime_type_list)
+    return;
+
+  if (clipboard_rdp->pending_server_formats)
+    {
+      if (clipboard_rdp->queued_server_formats)
+        g_debug ("[RDP.CLIPRDR] Replacing queued server FormatList");
+      g_clear_pointer (&clipboard_rdp->queued_server_formats, g_list_free);
+
+      g_debug ("[RDP.CLIPRDR] Queueing new FormatList");
+      clipboard_rdp->queued_server_formats = mime_type_list;
+
+      if (!clipboard_rdp->pending_server_formats_drop_id)
+        {
+          clipboard_rdp->pending_server_formats_drop_id =
+            g_timeout_add (MAX_WAIT_TIME, drop_pending_server_formats, clipboard_rdp);
+        }
+
+      return;
+    }
+
+  send_mime_type_list (clipboard_rdp, mime_type_list);
+}
+
 static CLIPRDR_FORMAT_DATA_RESPONSE *
 get_format_data_response (GrdClipboardRdp             *clipboard_rdp,
                           CLIPRDR_FORMAT_DATA_REQUEST *format_data_request)
@@ -924,12 +933,21 @@ static gboolean
 handle_format_list_response (gpointer user_data)
 {
   GrdClipboardRdp *clipboard_rdp = user_data;
+  GList *queued_server_formats;
 
-  update_allowed_server_formats (clipboard_rdp, TRUE);
+  if (clipboard_rdp->pending_server_formats)
+    update_allowed_server_formats (clipboard_rdp, TRUE);
+
+  g_clear_handle_id (&clipboard_rdp->pending_server_formats_drop_id,
+                     g_source_remove);
 
   clipboard_rdp->client_format_list_response_id = 0;
   ResetEvent (clipboard_rdp->format_list_response_received_event);
 
+  queued_server_formats = g_steal_pointer (&clipboard_rdp->queued_server_formats);
+  if (queued_server_formats)
+    send_mime_type_list (clipboard_rdp, queued_server_formats);
+
   return G_SOURCE_REMOVE;
 }
 
@@ -1489,6 +1507,7 @@ grd_clipboard_rdp_dispose (GObject *object)
     g_free ((uint8_t *) clipboard_rdp->format_data_response->requestedFormatData);
   g_clear_pointer (&clipboard_rdp->format_data_response, g_free);
 
+  g_clear_pointer (&clipboard_rdp->queued_server_formats, g_list_free);
   g_clear_pointer (&clipboard_rdp->pending_server_formats, g_list_free);
   grd_rdp_fuse_clipboard_clear_selection (clipboard_rdp->rdp_fuse_clipboard);
   g_hash_table_foreach_remove (clipboard_rdp->format_data_cache,
@@ -1511,6 +1530,7 @@ grd_clipboard_rdp_dispose (GObject *object)
   g_clear_pointer (&clipboard_rdp->system, ClipboardDestroy);
   g_clear_pointer (&clipboard_rdp->cliprdr_context, cliprdr_server_context_free);
 
+  g_clear_handle_id (&clipboard_rdp->pending_server_formats_drop_id, g_source_remove);
   g_clear_handle_id (&clipboard_rdp->server_format_list_update_id, g_source_remove);
   g_clear_handle_id (&clipboard_rdp->server_format_data_request_id, g_source_remove);
   g_clear_handle_id (&clipboard_rdp->client_format_list_response_id, g_source_remove);


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