[libsoup/carlosgc/connection-state] connection: rework state changes




commit 6b9b7c4045b33ac4f61f3539a59c39536e21737a
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Tue May 11 14:47:48 2021 +0200

    connection: rework state changes
    
    Make soup_connection_set_state() private and the property read only. The
    changes to IN_USE and IDLE states are now done by
    soup_connection_set_in_use() that is called every time the connection is
    assigned or unassigned to a message. When the connection is no longer
    used by any message, it switches to IDLE state if it's reusable or
    DISCONNECT otherwise. This simplifies the way the state is changed,
    soup_connection_set_state() is now a simpler setter, it can't be called
    recursively anymore. soup_connection_get_state() has been simplified
    too, it was weird that a getter could also change the state, so it's now
    a simpler getter that returns the current state. Callers can check if
    the idle connection is still open if they need it by using
    soup_connection_is_idle_open(). This removes the
    SOUP_CONNECTION_REMOTE_DISCONNECTED state that is no longer used.

 libsoup/soup-connection.c | 100 +++++++++++++++++++++++-----------------------
 libsoup/soup-connection.h |   7 ++--
 libsoup/soup-message.c    |   2 +
 libsoup/soup-session.c    |  26 ++++--------
 tests/connection-test.c   |   1 -
 5 files changed, 62 insertions(+), 74 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index a7666246..c5283b6d 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -38,6 +38,7 @@ typedef struct {
        SoupConnectionState state;
        time_t       unused_timeout;
        GSource     *idle_timeout_src;
+        guint        in_use;
        gboolean     reusable;
 
        GCancellable *cancellable;
@@ -135,9 +136,6 @@ soup_connection_set_property (GObject *object, guint prop_id,
        case PROP_SOCKET_PROPERTIES:
                priv->socket_props = g_value_dup_boxed (value);
                break;
-       case PROP_STATE:
-               soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
-               break;
        case PROP_SSL:
                priv->ssl = g_value_get_boolean (value);
                break;
@@ -254,8 +252,9 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                g_param_spec_enum ("state",
                                   "Connection state",
                                   "Current state of connection",
-                                  SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
-                                  G_PARAM_READWRITE |
+                                  SOUP_TYPE_CONNECTION_STATE,
+                                   SOUP_CONNECTION_NEW,
+                                  G_PARAM_READABLE |
                                   G_PARAM_STATIC_STRINGS);
         properties[PROP_SSL] =
                g_param_spec_boolean ("ssl",
@@ -328,6 +327,22 @@ stop_idle_timer (SoupConnectionPrivate *priv)
        }
 }
 
+static void
+soup_connection_set_state (SoupConnection     *conn,
+                           SoupConnectionState state)
+{
+        SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+
+        if (priv->state == state)
+                return;
+
+        priv->state = state;
+        if (priv->state == SOUP_CONNECTION_IDLE)
+                start_idle_timer (conn);
+
+        g_object_notify_by_pspec (G_OBJECT (conn), properties[PROP_STATE]);
+}
+
 static void
 current_msg_got_body (SoupMessage *msg, gpointer user_data)
 {
@@ -837,8 +852,7 @@ soup_connection_disconnect (SoupConnection *conn)
         priv = soup_connection_get_instance_private (conn);
 
         old_state = priv->state;
-        if (old_state != SOUP_CONNECTION_DISCONNECTED)
-                soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
+        soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
 
         if (priv->cancellable) {
                 g_cancellable_cancel (priv->cancellable);
@@ -933,19 +947,21 @@ soup_connection_is_via_proxy (SoupConnection *conn)
        return priv->proxy_uri != NULL;
 }
 
-static gboolean
-is_idle_connection_disconnected (SoupConnection *conn)
+gboolean
+soup_connection_is_idle_open (SoupConnection *conn)
 {
-       SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+        SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
        GInputStream *istream;
        char buffer[1];
        GError *error = NULL;
 
+        g_assert (priv->state == SOUP_CONNECTION_IDLE);
+
        if (!g_socket_is_connected (soup_connection_get_socket (conn)))
-               return TRUE;
+               return FALSE;
 
        if (priv->unused_timeout && priv->unused_timeout < time (NULL))
-               return TRUE;
+               return FALSE;
 
        istream = g_io_stream_get_input_stream (priv->iostream);
 
@@ -963,64 +979,48 @@ is_idle_connection_disconnected (SoupConnection *conn)
                                                  NULL, &error);
        if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                g_clear_error (&error);
-               return TRUE;
+               return FALSE;
        }
 
        g_error_free (error);
 
-       return FALSE;
+       return TRUE;
 }
 
 SoupConnectionState
 soup_connection_get_state (SoupConnection *conn)
 {
-       SoupConnectionPrivate *priv;
-
-       g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
-                             SOUP_CONNECTION_DISCONNECTED);
-       priv = soup_connection_get_instance_private (conn);
-
-       if (priv->state == SOUP_CONNECTION_IDLE &&
-           is_idle_connection_disconnected (conn))
-               soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
+       SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
 
        return priv->state;
 }
 
 void
-soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
+soup_connection_set_in_use (SoupConnection *conn,
+                            gboolean        in_use)
 {
-       SoupConnectionPrivate *priv;
-
-       g_return_if_fail (SOUP_IS_CONNECTION (conn));
-       g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
-                         state <= SOUP_CONNECTION_DISCONNECTED);
+        SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
 
-        g_object_ref (conn);
-       g_object_freeze_notify (G_OBJECT (conn));
+        g_assert (in_use || priv->in_use > 0);
 
-       priv = soup_connection_get_instance_private (conn);
-
-       if (priv->current_msg) {
-               g_warn_if_fail (state == SOUP_CONNECTION_IDLE ||
-                               state == SOUP_CONNECTION_DISCONNECTED);
-               clear_current_msg (conn);
-       }
-
-       if (state == SOUP_CONNECTION_IDLE && !priv->reusable) {
-               /* This will recursively call set_state() */
-               soup_connection_disconnect (conn);
-       } else {
-               priv->state = state;
+        if (in_use)
+                priv->in_use++;
+        else
+                priv->in_use--;
 
-               if (priv->state == SOUP_CONNECTION_IDLE)
-                       start_idle_timer (conn);
+        if (priv->in_use > 0) {
+                if (priv->state == SOUP_CONNECTION_IDLE)
+                        soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+                return;
+        }
 
-               g_object_notify_by_pspec (G_OBJECT (conn), properties[PROP_STATE]);
-       }
+        if (priv->current_msg)
+                clear_current_msg (conn);
 
-       g_object_thaw_notify (G_OBJECT (conn));
-        g_object_unref (conn);
+        if (priv->reusable)
+                soup_connection_set_state (conn, SOUP_CONNECTION_IDLE);
+        else
+                soup_connection_disconnect (conn);
 }
 
 void
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 9c556029..e0c5ab18 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -20,7 +20,6 @@ typedef enum {
        SOUP_CONNECTION_CONNECTING,
        SOUP_CONNECTION_IDLE,
        SOUP_CONNECTION_IN_USE,
-       SOUP_CONNECTION_REMOTE_DISCONNECTED,
        SOUP_CONNECTION_DISCONNECTED
 } SoupConnectionState;
 
@@ -57,9 +56,9 @@ gboolean        soup_connection_is_via_proxy   (SoupConnection   *conn);
 gboolean        soup_connection_is_tunnelled   (SoupConnection   *conn);
 
 SoupConnectionState soup_connection_get_state  (SoupConnection   *conn);
-void                soup_connection_set_state  (SoupConnection   *conn,
-                                               SoupConnectionState state);
-
+void            soup_connection_set_in_use     (SoupConnection   *conn,
+                                                gboolean          in_use);
+gboolean        soup_connection_is_idle_open   (SoupConnection   *conn);
 void            soup_connection_set_reusable   (SoupConnection   *conn,
                                                 gboolean          reusable);
 
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index ef4d782a..c39522a8 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -1421,12 +1421,14 @@ soup_message_set_connection (SoupMessage    *msg,
                g_signal_handlers_disconnect_by_data (priv->connection, msg);
                 priv->io_data = NULL;
                g_object_remove_weak_pointer (G_OBJECT (priv->connection), (gpointer*)&priv->connection);
+                soup_connection_set_in_use (priv->connection, FALSE);
        }
 
        priv->connection = conn;
        if (!priv->connection)
                return;
 
+        soup_connection_set_in_use (priv->connection, TRUE);
         priv->last_connection_id = soup_connection_get_id (priv->connection);
 
        g_object_add_weak_pointer (G_OBJECT (priv->connection), (gpointer*)&priv->connection);
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index b027a19b..9be57b21 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1296,8 +1296,6 @@ message_restarted (SoupMessage *msg, gpointer user_data)
        if (conn &&
            (!soup_message_is_keepalive (msg) ||
             SOUP_STATUS_IS_REDIRECTION (soup_message_get_status (msg)))) {
-               if (soup_connection_get_state (conn) == SOUP_CONNECTION_IN_USE)
-                       soup_connection_set_state (conn, SOUP_CONNECTION_IDLE);
                 soup_message_set_connection (item->msg, NULL);
        }
 
@@ -1402,8 +1400,8 @@ soup_session_cleanup_connections (SoupSession *session,
        g_hash_table_iter_init (&iter, priv->conns);
        while (g_hash_table_iter_next (&iter, &conn, &host)) {
                state = soup_connection_get_state (conn);
-               if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
-                   (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
+                if (state == SOUP_CONNECTION_IDLE &&
+                    (cleanup_idle || !soup_connection_is_idle_open (conn))) {
                        conns = g_slist_prepend (conns, g_object_ref (conn));
                        g_hash_table_iter_remove (&iter);
                        drop_connection (session, host, conn);
@@ -1494,7 +1492,7 @@ connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data
        SoupSession *session = user_data;
        SoupConnection *conn = SOUP_CONNECTION (object);
 
-       if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
+       if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE && soup_connection_is_idle_open (conn))
                soup_session_kick_queue (session);
 }
 
@@ -1505,15 +1503,8 @@ soup_session_unqueue_item (SoupSession          *session,
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
        SoupSessionHost *host;
        GSList *f;
-        SoupConnection *conn;
 
-        conn = soup_message_get_connection (item->msg);
-       if (conn) {
-               if (soup_message_get_method (item->msg) != SOUP_METHOD_CONNECT ||
-                   !SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (item->msg)))
-                       soup_connection_set_state (conn, SOUP_CONNECTION_IDLE);
-                soup_message_set_connection (item->msg, NULL);
-       }
+        soup_message_set_connection (item->msg, NULL);
 
        if (item->state != SOUP_MESSAGE_FINISHED) {
                g_warning ("finished an item with state %d", item->state);
@@ -1744,6 +1735,7 @@ steal_preconnection (SoupSession          *session,
         if (!preconnect_item->connect_only || preconnect_item->state != SOUP_MESSAGE_CONNECTING)
                 return FALSE;
 
+        soup_message_set_connection (item->msg, conn);
         soup_message_set_connection (preconnect_item->msg, NULL);
         g_assert (preconnect_item->related == NULL);
         preconnect_item->related = soup_message_queue_item_ref (item);
@@ -1778,10 +1770,8 @@ get_connection_for_host (SoupSession *session,
 
                switch (soup_connection_get_state (conn)) {
                case SOUP_CONNECTION_IDLE:
-                       if (!need_new_connection) {
-                               soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+                       if (!need_new_connection && soup_connection_is_idle_open (conn))
                                return conn;
-                       }
                        break;
                case SOUP_CONNECTION_CONNECTING:
                        if (steal_preconnection (session, item, conn))
@@ -1902,7 +1892,6 @@ get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
        case SOUP_CONNECTION_NEW:
                break;
        case SOUP_CONNECTION_IDLE:
-       case SOUP_CONNECTION_REMOTE_DISCONNECTED:
        case SOUP_CONNECTION_DISCONNECTED:
                g_assert_not_reached ();
        }
@@ -2172,8 +2161,7 @@ soup_session_abort (SoupSession *session)
                SoupConnectionState state;
 
                state = soup_connection_get_state (conn);
-               if (state == SOUP_CONNECTION_IDLE ||
-                   state == SOUP_CONNECTION_REMOTE_DISCONNECTED) {
+               if (state == SOUP_CONNECTION_IDLE) {
                        conns = g_slist_prepend (conns, g_object_ref (conn));
                        g_hash_table_iter_remove (&iter);
                        drop_connection (session, host, conn);
diff --git a/tests/connection-test.c b/tests/connection-test.c
index cd9c71f1..2e90cce1 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -701,7 +701,6 @@ connection_state_changed (SoupConnection      *conn,
                                  "Unexpected transition: %s -> %s\n",
                                  state_names[*state], state_names[new_state]);
                break;
-       case SOUP_CONNECTION_REMOTE_DISCONNECTED:
        case SOUP_CONNECTION_DISCONNECTED:
                soup_test_assert (FALSE,
                                  "Unexpected transition: %s -> %s\n",


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