[glib: 1/2] gsocketlistener: Fix multiple returns of GTask when accepting sockets



commit 30ccfac9cf594c7bb224ff38ce109b442fb10a3c
Author: Philip Withnall <withnall endlessm com>
Date:   Fri Feb 8 00:05:52 2019 +0000

    gsocketlistener: Fix multiple returns of GTask when accepting sockets
    
    When calling g_socket_listener_accept_socket_async() on a
    GSocketListener with multiple sockets, the accept_ready() callback is
    called for the first incoming connection on each socket. It will return
    success/failure for the entire accept_socket_async() GTask, and then
    free the GSources for listening for incoming connections on the other
    sockets in the GSocketListener. The GSources are freed when the GTask is
    finalised.
    
    However, if incoming connections arrive for multiple sockets within the
    same GMainContext iteration, accept_ready() will be called multiple
    times, and will call g_task_return_*() multiple times, before the GTask
    is finalised. Calling g_task_return_*() multiple times is not allowed.
    
    Propagate the first success/failure, as before, but then ignore all
    subsequent incoming connections until the GTask is finalised.
    
    Signed-off-by: Philip Withnall <withnall endlessm com>

 gio/gsocketlistener.c | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)
---
diff --git a/gio/gsocketlistener.c b/gio/gsocketlistener.c
index ab17678a9..a2c879d9c 100644
--- a/gio/gsocketlistener.c
+++ b/gio/gsocketlistener.c
@@ -773,6 +773,19 @@ g_socket_listener_accept (GSocketListener  *listener,
   return connection;
 }
 
+typedef struct
+{
+  GList *sources;  /* (element-type GSource) */
+  gboolean returned_yet;
+} AcceptSocketAsyncData;
+
+static void
+accept_socket_async_data_free (AcceptSocketAsyncData *data)
+{
+  free_sources (data->sources);
+  g_free (data);
+}
+
 static gboolean
 accept_ready (GSocket      *accept_socket,
              GIOCondition  condition,
@@ -782,6 +795,12 @@ accept_ready (GSocket      *accept_socket,
   GError *error = NULL;
   GSocket *socket;
   GObject *source_object;
+  AcceptSocketAsyncData *data = g_task_get_task_data (task);
+
+  /* Don’t call g_task_return_*() multiple times if we have multiple incoming
+   * connections in the same #GMainContext iteration. */
+  if (data->returned_yet)
+    return G_SOURCE_REMOVE;
 
   socket = g_socket_accept (accept_socket, g_task_get_cancellable (task), &error);
   if (socket)
@@ -798,8 +817,10 @@ accept_ready (GSocket      *accept_socket,
       g_task_return_error (task, error);
     }
 
+  data->returned_yet = TRUE;
   g_object_unref (task);
-  return FALSE;
+
+  return G_SOURCE_REMOVE;
 }
 
 /**
@@ -824,8 +845,8 @@ g_socket_listener_accept_socket_async (GSocketListener     *listener,
                                       gpointer             user_data)
 {
   GTask *task;
-  GList *sources;
   GError *error = NULL;
+  AcceptSocketAsyncData *data = NULL;
 
   task = g_task_new (listener, cancellable, callback, user_data);
   g_task_set_source_tag (task, g_socket_listener_accept_socket_async);
@@ -837,12 +858,15 @@ g_socket_listener_accept_socket_async (GSocketListener     *listener,
       return;
     }
 
-  sources = add_sources (listener,
+  data = g_new0 (AcceptSocketAsyncData, 1);
+  data->returned_yet = FALSE;
+  data->sources = add_sources (listener,
                         accept_ready,
                         task,
                         cancellable,
                         g_main_context_get_thread_default ());
-  g_task_set_task_data (task, sources, (GDestroyNotify) free_sources);
+  g_task_set_task_data (task, g_steal_pointer (&data),
+                        (GDestroyNotify) accept_socket_async_data_free);
 }
 
 /**


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