[libsoup] SoupConnection: fix a race condition with non-keepalive messages
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] SoupConnection: fix a race condition with non-keepalive messages
- Date: Thu, 18 Oct 2012 16:46:53 +0000 (UTC)
commit aa8e1dc5127b56bd96e9d3068a6bfeb7c1a07a94
Author: Dan Winship <danw gnome org>
Date: Tue Oct 2 08:57:52 2012 -0400
SoupConnection: fix a race condition with non-keepalive messages
When a SoupSession sets a connection back to IDLE on a non-keepalive
message, the connection then disconnects itself. However, it had been
briefly setting its state to IDLE before disconnecting. Although this
wasn't supposed to be observable (it doesn't emit a notification), in
a SoupSessionSync, it could be observed by another thread, which might
then try to claim the connection to send another request on, causing
problems when the first thread then disconnected it.
Fix this by inlining clear_current_item() into
soup_connection_set_state() and reordering the code to not change
priv->state until after deciding whether or not it's going to
disconnect.
https://bugzilla.gnome.org/show_bug.cgi?id=684238
libsoup/soup-connection.c | 93 ++++++++++++++++++++------------------------
1 files changed, 42 insertions(+), 51 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index d2746e8..1889a94 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -66,7 +66,6 @@ enum {
};
static void stop_idle_timer (SoupConnectionPrivate *priv);
-static void clear_current_item (SoupConnection *conn);
/* Number of seconds after which we close a connection that hasn't yet
* been used.
@@ -99,13 +98,7 @@ soup_connection_dispose (GObject *object)
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
stop_idle_timer (priv);
- /* Make sure clear_current_item doesn't re-establish the timeout */
- priv->idle_timeout = 0;
- if (priv->cur_item) {
- g_warning ("Disposing connection with cur_item set");
- clear_current_item (conn);
- }
if (priv->socket) {
g_warning ("Disposing connection while connected");
soup_connection_disconnect (conn);
@@ -417,45 +410,6 @@ set_current_item (SoupConnection *conn, SoupMessageQueueItem *item)
}
static void
-clear_current_item (SoupConnection *conn)
-{
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-
- g_object_freeze_notify (G_OBJECT (conn));
-
- priv->unused_timeout = 0;
- start_idle_timer (conn);
-
- if (priv->cur_item) {
- SoupMessageQueueItem *item;
-
- item = priv->cur_item;
- priv->cur_item = NULL;
- g_object_notify (G_OBJECT (conn), "message");
-
- g_signal_handlers_disconnect_by_func (item->msg, G_CALLBACK (current_item_restarted), conn);
-
- if (item->msg->method == SOUP_METHOD_CONNECT &&
- SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) {
- soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL);
-
- /* We're now effectively no longer proxying */
- soup_uri_free (priv->proxy_uri);
- priv->proxy_uri = NULL;
-
- /* Nor are we actually IDLE... */
- if (priv->state == SOUP_CONNECTION_IDLE)
- priv->state = SOUP_CONNECTION_IN_USE;
- }
-
- if (!soup_message_is_keepalive (item->msg) || !priv->reusable)
- soup_connection_disconnect (conn);
- }
-
- g_object_thaw_notify (G_OBJECT (conn));
-}
-
-static void
proxy_socket_event (SoupSocket *socket,
GSocketClientEvent event,
GIOStream *connection,
@@ -938,6 +892,7 @@ void
soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
{
SoupConnectionPrivate *priv;
+ SoupConnectionState old_state;
g_return_if_fail (SOUP_IS_CONNECTION (conn));
g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
@@ -946,13 +901,49 @@ soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
g_object_freeze_notify (G_OBJECT (conn));
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- priv->state = state;
- if (state == SOUP_CONNECTION_IDLE ||
- state == SOUP_CONNECTION_DISCONNECTED)
- clear_current_item (conn);
+ old_state = priv->state;
+
+ if (old_state == SOUP_CONNECTION_IN_USE)
+ priv->unused_timeout = 0;
+
+ if (priv->cur_item) {
+ SoupMessageQueueItem *item;
+
+ g_warn_if_fail (state == SOUP_CONNECTION_IDLE ||
+ state == SOUP_CONNECTION_DISCONNECTED);
+
+ item = priv->cur_item;
+ priv->cur_item = NULL;
+ g_object_notify (G_OBJECT (conn), "message");
+
+ g_signal_handlers_disconnect_by_func (item->msg, G_CALLBACK (current_item_restarted), conn);
+
+ if (item->msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) {
+ soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL);
+
+ /* We're now effectively no longer proxying */
+ soup_uri_free (priv->proxy_uri);
+ priv->proxy_uri = NULL;
+
+ /* Nor are we actually IDLE... */
+ if (state == SOUP_CONNECTION_IDLE)
+ state = SOUP_CONNECTION_IN_USE;
+ }
+
+ if (!soup_message_is_keepalive (item->msg) || !priv->reusable)
+ soup_connection_disconnect (conn);
+ }
+
+ if (priv->state == old_state && priv->state != state) {
+ priv->state = state;
+
+ if (state == SOUP_CONNECTION_IDLE)
+ start_idle_timer (conn);
- if (priv->state == state)
g_object_notify (G_OBJECT (conn), "state");
+ }
+
g_object_thaw_notify (G_OBJECT (conn));
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]