soup-message-io: keep cancellable alive for duration of io_run_until
authorDan Winship <danw@gnome.org>
Tue, 15 May 2012 16:20:19 +0000 (12:20 -0400)
committerDan Winship <danw@gnome.org>
Tue, 15 May 2012 16:23:32 +0000 (12:23 -0400)
io_run() passes io->cancellable to io_run_until(), but io may be
destroyed (and cancellable unreffed) before io_run_until() returns,
causing it to eventually call g_cancellable_is_cancelled() on garbage.
Fix by reffing it around the io_run_until() call (though really this
is just another example of "SoupMessageIOData needs to be
refcounted".)

Tweak misc-test to test this case.

Fixes crasher in rygel test case. Based on a patch from Ray Strode.
https://bugzilla.gnome.org/show_bug.cgi?id=676038

libsoup/soup-message-io.c
tests/misc-test.c

index 455265e..7a554df 100644 (file)
@@ -886,6 +886,7 @@ io_run (SoupMessage *msg, gpointer user_data)
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
        SoupMessageIOData *io = priv->io_data;
        GError *error = NULL;
+       GCancellable *cancellable;
 
        if (io->io_source) {
                g_source_destroy (io->io_source);
@@ -894,11 +895,12 @@ io_run (SoupMessage *msg, gpointer user_data)
        }
 
        g_object_ref (msg);
+       cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL;
 
        if (io_run_until (msg,
                          SOUP_MESSAGE_IO_STATE_DONE,
                          SOUP_MESSAGE_IO_STATE_DONE,
-                         io->cancellable, &error)) {
+                         cancellable, &error)) {
                soup_message_io_finished (msg);
        } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                g_clear_error (&error);
@@ -909,6 +911,8 @@ io_run (SoupMessage *msg, gpointer user_data)
        }
 
        g_object_unref (msg);
+       g_clear_object (&cancellable);
+
        return FALSE;
 }
 
index 39d63d3..a2b9937 100644 (file)
@@ -857,11 +857,20 @@ cancel_message_thread (gpointer msg)
 }
 
 static void
+set_done (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       gboolean *done = user_data;
+
+       *done = TRUE;
+}
+
+static void
 do_cancel_while_reading_test_for_session (SoupSession *session)
 {
        SoupMessage *msg;
        GThread *thread = NULL;
        SoupURI *uri;
+       gboolean done = FALSE;
 
        uri = soup_uri_new_with_base (base_uri, "/slow");
        msg = soup_message_new_from_uri ("GET", uri);
@@ -875,7 +884,14 @@ do_cancel_while_reading_test_for_session (SoupSession *session)
        else
                thread = g_thread_new ("cancel_message_thread", cancel_message_thread, msg);
 
-       soup_session_send_message (session, msg);
+       /* We intentionally don't use soup_session_send_message() here,
+        * because it holds an extra ref on the SoupMessageQueueItem
+        * relative to soup_session_queue_message().
+        */
+       g_object_ref (msg);
+       soup_session_queue_message (session, msg, set_done, &done);
+       while (!done)
+               g_main_context_iteration (NULL, TRUE);
 
        if (msg->status_code != SOUP_STATUS_CANCELLED) {
                debug_printf (1, "      FAILED: %d %s (expected Cancelled)\n",