[libsoup] soup-message-io: do an async close when doing non-blocking I/O



commit add13ea00e31f9f78d8dd4b26109575a7500dcf6
Author: Dan Winship <danw gnome org>
Date:   Mon Jun 9 08:52:38 2014 -0400

    soup-message-io: do an async close when doing non-blocking I/O
    
    When using chunked encoding, SoupBodyOutputStream needs to write the
    final "0" chunk when it's closed, and thus may block. So we have to do
    an async close in the non-blocking case.
    
    (This also requires changing continue-test to not trace "finished"
    events, since it becomes unpredicatable exactly when they'll happen
    now.)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=727138

 libsoup/soup-message-io.c |   71 ++++++++++++++++++++++++++++++++++++++++----
 tests/continue-test.c     |   33 ---------------------
 2 files changed, 64 insertions(+), 40 deletions(-)
---
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 600cd85..db98dc2 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -80,6 +80,9 @@ typedef struct {
        GSource *unpause_source;
        gboolean paused;
 
+       GCancellable *async_close_wait;
+       GError       *async_close_error;
+
        SoupMessageGetHeadersFn   get_headers_cb;
        SoupMessageParseHeadersFn parse_headers_cb;
        gpointer                  header_data;
@@ -87,6 +90,7 @@ typedef struct {
        gpointer                  completion_data;
 } SoupMessageIOData;
        
+static void io_run (SoupMessage *msg, gboolean blocking);
 
 #define RESPONSE_BLOCK_SIZE 8192
 
@@ -275,6 +279,33 @@ soup_message_setup_body_istream (GInputStream *body_stream,
        return istream;
 }
 
+static void
+closed_async (GObject      *source,
+             GAsyncResult *result,
+             gpointer      user_data)
+{
+       GOutputStream *body_ostream = G_OUTPUT_STREAM (source);
+       SoupMessage *msg = user_data;
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       SoupMessageIOData *io = priv->io_data;
+       GCancellable *async_close_wait;
+
+       if (!io || !io->async_close_wait || io->body_ostream != body_ostream) {
+               g_object_unref (msg);
+               return;
+       }
+
+       g_output_stream_close_finish (body_ostream, result, &io->async_close_error);
+       g_clear_object (&io->body_ostream);
+
+       async_close_wait = io->async_close_wait;
+       io->async_close_wait = NULL;
+       g_cancellable_cancel (async_close_wait);
+       g_object_unref (async_close_wait);
+
+       g_object_unref (msg);
+}
+
 /*
  * There are two request/response formats: the basic request/response,
  * possibly with one or more unsolicited informational responses (such
@@ -316,6 +347,17 @@ io_write (SoupMessage *msg, gboolean blocking,
        SoupBuffer *chunk;
        gssize nwrote;
 
+       if (io->async_close_error) {
+               g_propagate_error (error, io->async_close_error);
+               io->async_close_error = NULL;
+               return FALSE;
+       } else if (io->async_close_wait) {
+               g_set_error_literal (error, G_IO_ERROR,
+                                    G_IO_ERROR_WOULD_BLOCK,
+                                    _("Operation would block"));
+               return FALSE;
+       }
+
        switch (io->write_state) {
        case SOUP_MESSAGE_IO_STATE_HEADERS:
                if (!io->write_buf->len) {
@@ -460,13 +502,27 @@ io_write (SoupMessage *msg, gboolean blocking,
 
        case SOUP_MESSAGE_IO_STATE_BODY_DONE:
                if (io->body_ostream) {
-                       if (!g_output_stream_close (io->body_ostream, cancellable, error))
-                               return FALSE;
-                       g_clear_object (&io->body_ostream);
+                       if (blocking) {
+                               if (!g_output_stream_close (io->body_ostream, cancellable, error))
+                                       return FALSE;
+                               g_clear_object (&io->body_ostream);
+                       } else {
+                               io->async_close_wait = g_cancellable_new ();
+                               if (io->async_context)
+                                       g_main_context_push_thread_default (io->async_context);
+                               g_output_stream_close_async (io->body_ostream,
+                                                            G_PRIORITY_DEFAULT, cancellable,
+                                                            closed_async, g_object_ref (msg));
+                               if (io->async_context)
+                                       g_main_context_pop_thread_default (io->async_context);
+                       }
                }
 
                io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
                soup_message_wrote_body (msg);
+
+               if (io->async_close_wait)
+                       return TRUE;
                break;
 
 
@@ -782,6 +838,8 @@ soup_message_io_get_source (SoupMessage *msg, GCancellable *cancellable,
                base_source = g_timeout_source_new (0);
        } else if (io->paused) {
                base_source = NULL;
+       } else if (io->async_close_wait) {
+               base_source = g_cancellable_source_new (io->async_close_wait);
        } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) {
                GPollableInputStream *istream;
 
@@ -857,7 +915,7 @@ io_run_until (SoupMessage *msg, gboolean blocking,
 
        g_object_ref (msg);
 
-       while (progress && priv->io_data == io && !io->paused &&
+       while (progress && priv->io_data == io && !io->paused && !io->async_close_wait &&
               (io->read_state < read_state || io->write_state < write_state)) {
 
                if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
@@ -881,7 +939,8 @@ io_run_until (SoupMessage *msg, gboolean blocking,
                g_propagate_error (error, my_error);
                g_object_unref (msg);
                return FALSE;
-       } else if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+       } else if (!io->async_close_wait &&
+                  g_cancellable_set_error_if_cancelled (cancellable, error)) {
                g_object_unref (msg);
                return FALSE;
        } else if (priv->io_data != io) {
@@ -907,8 +966,6 @@ io_run_until (SoupMessage *msg, gboolean blocking,
        return done;
 }
 
-static void io_run (SoupMessage *msg, gboolean blocking);
-
 static gboolean
 io_run_ready (SoupMessage *msg, gpointer user_data)
 {
diff --git a/tests/continue-test.c b/tests/continue-test.c
index 5e0c660..1bf0774 100644
--- a/tests/continue-test.c
+++ b/tests/continue-test.c
@@ -44,7 +44,6 @@ EVENT_HANDLER (got_body)
 EVENT_HANDLER (wrote_informational)
 EVENT_HANDLER (wrote_headers)
 EVENT_HANDLER (wrote_body)
-EVENT_HANDLER (finished)
 
 static void
 do_message (const char *path, gboolean long_body,
@@ -90,8 +89,6 @@ do_message (const char *path, gboolean long_body,
                          G_CALLBACK (wrote_headers), "client");
        g_signal_connect (msg, "wrote_body",
                          G_CALLBACK (wrote_body), "client");
-       g_signal_connect (msg, "finished",
-                         G_CALLBACK (finished), "client");
 
        events = NULL;
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -158,10 +155,8 @@ do_test_unauth_short_noexpect_nopass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_CREATED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -175,10 +170,8 @@ do_test_unauth_long_noexpect_nopass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -194,10 +187,8 @@ do_test_unauth_short_expect_nopass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_CREATED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -209,10 +200,8 @@ do_test_unauth_long_expect_nopass (void)
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -226,10 +215,8 @@ do_test_auth_short_noexpect_nopass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -243,10 +230,8 @@ do_test_auth_long_noexpect_nopass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -258,10 +243,8 @@ do_test_auth_short_expect_nopass (void)
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -273,10 +256,8 @@ do_test_auth_long_expect_nopass (void)
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -290,7 +271,6 @@ do_test_auth_short_noexpect_pass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
                    "client-wrote_headers",
@@ -299,10 +279,8 @@ do_test_auth_short_noexpect_pass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_CREATED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -316,7 +294,6 @@ do_test_auth_long_noexpect_pass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
                    "client-wrote_headers",
@@ -325,10 +302,8 @@ do_test_auth_long_noexpect_pass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -340,7 +315,6 @@ do_test_auth_short_expect_pass (void)
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
                    "client-wrote_headers",
@@ -351,10 +325,8 @@ do_test_auth_short_expect_pass (void)
                    "server-got_body",
                    "server-wrote_headers", SOUP_STATUS_CREATED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -366,17 +338,14 @@ do_test_auth_long_expect_pass (void)
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
                    "server-wrote_body",
-                   "server-finished",
                    "client-got_headers",
                    "client-got_body",
-                   "client-finished",
                    NULL);
 }
 
@@ -423,8 +392,6 @@ request_started (SoupServer *server, SoupMessage *msg,
                          G_CALLBACK (wrote_headers), "server");
        g_signal_connect (msg, "wrote_body",
                          G_CALLBACK (wrote_body), "server");
-       g_signal_connect (msg, "finished",
-                         G_CALLBACK (finished), "server");
 }
 
 static gboolean


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