summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2011-04-20 10:18:57 -0400
committerDan Winship <danw@gnome.org>2011-04-25 11:11:14 -0400
commit4fccc7a2464075e83e572a7907cc11831cddd872 (patch)
tree5249a33895057e0995458a0b10e5b549375a000e
parent42f9135da10dffe6d26f1bc6c70fa41fb211b627 (diff)
downloadlibsoup-4fccc7a2464075e83e572a7907cc11831cddd872.tar.gz
soup_session_abort: fix a race condition in SoupSessionSync
soup_session_abort() on a SoupSessionSync would sometimes crash, because of races between the cancellation of the pending messages and the destruction of the open connections. Fix this by waiting for all of the messages to finish cancelling before we start closing the connections.
-rw-r--r--libsoup/soup-session-sync.c46
-rw-r--r--libsoup/soup-session.c24
-rw-r--r--libsoup/soup-session.h2
3 files changed, 64 insertions, 8 deletions
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
index 493d8c5a..8cc7ab5c 100644
--- a/libsoup/soup-session-sync.c
+++ b/libsoup/soup-session-sync.c
@@ -60,6 +60,7 @@ static void cancel_message (SoupSession *session, SoupMessage *msg,
guint status_code);
static void auth_required (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying);
+static void flush_queue (SoupSession *session);
G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
@@ -96,6 +97,8 @@ soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
session_class->send_message = send_message;
session_class->cancel_message = cancel_message;
session_class->auth_required = auth_required;
+ session_class->flush_queue = flush_queue;
+
object_class->finalize = finalize;
}
@@ -399,3 +402,46 @@ auth_required (SoupSession *session, SoupMessage *msg,
SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
auth_required (session, msg, auth, retrying);
}
+
+static void
+flush_queue (SoupSession *session)
+{
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
+ SoupMessageQueue *queue;
+ SoupMessageQueueItem *item;
+ GHashTable *current;
+ gboolean done = FALSE;
+
+ /* Record the current contents of the queue */
+ current = g_hash_table_new (NULL, NULL);
+ queue = soup_session_get_queue (session);
+ for (item = soup_message_queue_first (queue);
+ item;
+ item = soup_message_queue_next (queue, item))
+ g_hash_table_insert (current, item, item);
+
+ /* Cancel everything */
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->flush_queue (session);
+
+ /* Wait until all of the items in @current have been removed
+ * from the queue. (This is not the same as "wait for the
+ * queue to be empty", because the app may queue new requests
+ * in response to the cancellation of the old ones. We don't
+ * try to cancel those requests as well, since we'd likely
+ * just end up looping forever.)
+ */
+ g_mutex_lock (priv->lock);
+ do {
+ done = TRUE;
+ for (item = soup_message_queue_first (queue);
+ item;
+ item = soup_message_queue_next (queue, item)) {
+ if (g_hash_table_lookup (current, item))
+ done = FALSE;
+ }
+
+ if (!done)
+ g_cond_wait (priv->cond, priv->lock);
+ } while (!done);
+ g_mutex_unlock (priv->lock);
+}
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 4f9d975f..ba98e653 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -111,6 +111,7 @@ static void cancel_message (SoupSession *session, SoupMessage *msg,
guint status_code);
static void auth_required (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying);
+static void flush_queue (SoupSession *session);
static void auth_manager_authenticate (SoupAuthManager *manager,
SoupMessage *msg, SoupAuth *auth,
@@ -257,6 +258,7 @@ soup_session_class_init (SoupSessionClass *session_class)
session_class->requeue_message = requeue_message;
session_class->cancel_message = cancel_message;
session_class->auth_required = auth_required;
+ session_class->flush_queue = flush_queue;
/* virtual method override */
object_class->dispose = dispose;
@@ -1751,6 +1753,20 @@ gather_conns (gpointer key, gpointer host, gpointer data)
*conns = g_slist_prepend (*conns, g_object_ref (conn));
}
+static void
+flush_queue (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item)) {
+ soup_session_cancel_message (session, item->msg,
+ SOUP_STATUS_CANCELLED);
+ }
+}
+
/**
* soup_session_abort:
* @session: the session
@@ -1761,18 +1777,12 @@ void
soup_session_abort (SoupSession *session)
{
SoupSessionPrivate *priv;
- SoupMessageQueueItem *item;
GSList *conns, *c;
g_return_if_fail (SOUP_IS_SESSION (session));
priv = SOUP_SESSION_GET_PRIVATE (session);
- for (item = soup_message_queue_first (priv->queue);
- item;
- item = soup_message_queue_next (priv->queue, item)) {
- soup_session_cancel_message (session, item->msg,
- SOUP_STATUS_CANCELLED);
- }
+ SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
/* Close all connections */
g_mutex_lock (priv->host_lock);
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index fe078926..4b6661f7 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -49,9 +49,9 @@ typedef struct {
void (*auth_required) (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying);
+ void (*flush_queue) (SoupSession *session);
/* Padding for future expansion */
- void (*_libsoup_reserved2) (void);
void (*_libsoup_reserved3) (void);
void (*_libsoup_reserved4) (void);
} SoupSessionClass;