fix #4680
authorChristian Grothoff <christian@grothoff.org>
Mon, 26 Sep 2016 14:50:19 +0000 (14:50 +0000)
committerChristian Grothoff <christian@grothoff.org>
Mon, 26 Sep 2016 14:50:19 +0000 (14:50 +0000)
src/util/service_new.c

index c547e3bf3c83c681510b043a0db7158e8b1acb38..0b9e8fd82fa94345e6c62b0df1e4c04ef97907b6 100644 (file)
@@ -254,6 +254,12 @@ struct GNUNET_SERVICE_Client
    */
   struct GNUNET_SCHEDULER_Task *warn_task;
 
+  /**
+   * Task run to finish dropping the client after the stack has
+   * properly unwound.
+   */
+  struct GNUNET_SCHEDULER_Task *drop_task;
+
   /**
    * Task that receives data from the client to
    * pass it to the handlers.
@@ -1951,7 +1957,7 @@ warn_no_client_continue (void *cls)
  *
  * @param cls closure with the `struct GNUNET_SERVICE_Client *`
  * @param message the actual message
- * @return #GNUNET_OK on success (always)
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the client was dropped
  */
 static int
 service_client_mst_cb (void *cls,
@@ -1970,6 +1976,8 @@ service_client_mst_cb (void *cls,
                                    client);
   GNUNET_MQ_inject_message (client->mq,
                             message);
+  if (NULL != client->drop_task)
+    return GNUNET_SYSERR;
   return GNUNET_OK;
 }
 
@@ -1994,8 +2002,11 @@ service_client_recv (void *cls)
   if (GNUNET_SYSERR == ret)
   {
     /* client closed connection (or IO error) */
-    GNUNET_assert (GNUNET_NO == client->needs_continue);
-    GNUNET_SERVICE_client_drop (client);
+    if (NULL == client->drop_task)
+    {
+      GNUNET_assert (GNUNET_NO == client->needs_continue);
+      GNUNET_SERVICE_client_drop (client);
+    }
     return;
   }
   if (GNUNET_NO == ret)
@@ -2312,6 +2323,35 @@ GNUNET_SERVICE_client_disable_continue_warning (struct GNUNET_SERVICE_Client *c)
 }
 
 
+/**
+ * Asynchronously finish dropping the client.
+ *
+ * @param cls the `struct GNUNET_SERVICE_Client`.
+ */
+static void
+finish_client_drop (void *cls)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+  struct GNUNET_SERVICE_Handle *sh = c->sh;
+
+  GNUNET_MST_destroy (c->mst);
+  GNUNET_MQ_destroy (c->mq);
+  if (GNUNET_NO == c->persist)
+  {
+    GNUNET_break (GNUNET_OK ==
+                 GNUNET_NETWORK_socket_close (c->sock));
+  }
+  else
+  {
+    GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
+  }
+  GNUNET_free (c);
+  if ( (GNUNET_YES == sh->got_shutdown) &&
+       (GNUNET_NO == have_non_monitor_clients (sh)) )
+    GNUNET_SERVICE_shutdown (sh);
+}
+
+
 /**
  * Ask the server to disconnect from the given client.  This is the
  * same as returning #GNUNET_SYSERR within the check procedure when
@@ -2327,6 +2367,12 @@ GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
 {
   struct GNUNET_SERVICE_Handle *sh = c->sh;
 
+  if (NULL != c->drop_task)
+  {
+    /* asked to drop twice! */
+    GNUNET_break (0);
+    return;
+  }
   GNUNET_CONTAINER_DLL_remove (sh->clients_head,
                                sh->clients_tail,
                                c);
@@ -2348,21 +2394,8 @@ GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
     GNUNET_SCHEDULER_cancel (c->send_task);
     c->send_task = NULL;
   }
-  GNUNET_MST_destroy (c->mst);
-  GNUNET_MQ_destroy (c->mq);
-  if (GNUNET_NO == c->persist)
-  {
-    GNUNET_break (GNUNET_OK ==
-                 GNUNET_NETWORK_socket_close (c->sock));
-  }
-  else
-  {
-    GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
-  }
-  GNUNET_free (c);
-  if ( (GNUNET_YES == sh->got_shutdown) &&
-       (GNUNET_NO == have_non_monitor_clients (sh)) )
-    GNUNET_SERVICE_shutdown (sh);
+  c->drop_task = GNUNET_SCHEDULER_add_now (&finish_client_drop,
+                                           c);
 }