[libsoup] soup-message-queue: add a priority system to the message queue



commit c146806dc176f81be0c7554d67f9e981ee07a3b9
Author: Sergio Villar Senin <svillar igalia com>
Date:   Wed Apr 17 10:15:59 2013 +0200

    soup-message-queue: add a priority system to the message queue
    
    Clients can specify a priority for each message added to the SoupSession,
    which will determine the order in which it is processed by the
    session's message processing queue.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=696277

 docs/reference/libsoup-2.4-sections.txt |  5 ++
 libsoup/libsoup-2.4.sym                 |  3 ++
 libsoup/soup-message-private.h          |  2 +
 libsoup/soup-message-queue.c            | 25 +++++++--
 libsoup/soup-message-queue.h            |  3 +-
 libsoup/soup-message.c                  | 91 +++++++++++++++++++++++++++++++++
 libsoup/soup-message.h                  | 18 +++++++
 tests/session-test.c                    | 57 +++++++++++++++++++++
 8 files changed, 200 insertions(+), 4 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 9a85642..4b6ebfd 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -37,6 +37,10 @@ soup_message_set_chunk_allocator
 soup_message_disable_feature
 soup_message_get_soup_request
 <SUBSECTION>
+SoupMessagePriority
+soup_message_get_priority
+soup_message_set_priority
+<SUBSECTION>
 SOUP_MESSAGE_METHOD
 SOUP_MESSAGE_URI
 SOUP_MESSAGE_HTTP_VERSION
@@ -45,6 +49,7 @@ SOUP_MESSAGE_STATUS_CODE
 SOUP_MESSAGE_REASON_PHRASE
 SOUP_MESSAGE_SERVER_SIDE
 SOUP_MESSAGE_FIRST_PARTY
+SOUP_MESSAGE_PRIORITY
 SOUP_MESSAGE_REQUEST_BODY
 SOUP_MESSAGE_REQUEST_HEADERS
 SOUP_MESSAGE_RESPONSE_BODY
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index 22af160..d0f22f6 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -231,6 +231,7 @@ soup_message_get_first_party
 soup_message_get_flags
 soup_message_get_https_status
 soup_message_get_http_version
+soup_message_get_priority
 soup_message_get_soup_request
 soup_message_get_type
 soup_message_get_uri
@@ -273,11 +274,13 @@ soup_message_io_cleanup
 soup_message_is_keepalive
 soup_message_new
 soup_message_new_from_uri
+soup_message_priority_get_type
 soup_message_restarted
 soup_message_set_chunk_allocator
 soup_message_set_first_party
 soup_message_set_flags
 soup_message_set_http_version
+soup_message_set_priority
 soup_message_set_redirect
 soup_message_set_request
 soup_message_set_response
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index de7cb7d..356b96d 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -41,6 +41,8 @@ typedef struct {
        GTlsCertificateFlags  tls_errors;
 
        SoupRequest       *request;
+
+       SoupMessagePriority priority;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, 
SoupMessagePrivate))
 
diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c
index 8b1ebaf..3dced0e 100644
--- a/libsoup/soup-message-queue.c
+++ b/libsoup/soup-message-queue.c
@@ -89,6 +89,7 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
        item->callback = callback;
        item->callback_data = user_data;
        item->cancellable = g_cancellable_new ();
+       item->priority = soup_message_get_priority (msg);
 
        g_signal_connect (msg, "restarted",
                          G_CALLBACK (queue_message_restarted), item);
@@ -101,9 +102,27 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
 
        g_mutex_lock (&queue->mutex);
        if (queue->head) {
-               queue->tail->next = item;
-               item->prev = queue->tail;
-               queue->tail = item;
+               SoupMessageQueueItem *it = queue->head;
+
+               while (it && it->priority >= item->priority)
+                       it = it->next;
+
+               if (!it) {
+                       if (queue->tail) {
+                               queue->tail->next = item;
+                               item->prev = queue->tail;
+                       } else
+                               queue->head = item;
+                       queue->tail = item;
+               } else {
+                       if (it != queue->head)
+                               it->prev->next = item;
+                       else
+                               queue->head = item;
+                       item->prev = it->prev;
+                       it->prev = item;
+                       item->next = it;
+               }
        } else
                queue->head = queue->tail = item;
 
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index 135a6aa..d965e51 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -52,7 +52,8 @@ struct _SoupMessageQueueItem {
 
        /*< private >*/
        guint removed              : 1;
-       guint ref_count            : 31;
+       guint priority             : 3;
+       guint ref_count            : 28;
        SoupMessageQueueItem *prev, *next;
        SoupMessageQueueItem *related;
 };
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index d30fd37..b65dd76 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -135,6 +135,7 @@ enum {
        PROP_RESPONSE_HEADERS,
        PROP_TLS_CERTIFICATE,
        PROP_TLS_ERRORS,
+       PROP_PRIORITY,
 
        LAST_PROP
 };
@@ -145,6 +146,7 @@ soup_message_init (SoupMessage *msg)
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
        priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
+       priv->priority = SOUP_MESSAGE_PRIORITY_NORMAL;
 
        msg->request_body = soup_message_body_new ();
        msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
@@ -236,6 +238,9 @@ soup_message_set_property (GObject *object, guint prop_id,
                else if (priv->tls_certificate)
                        priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
                break;
+       case PROP_PRIORITY:
+               priv->priority = g_value_get_enum (value);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -292,6 +297,9 @@ soup_message_get_property (GObject *object, guint prop_id,
        case PROP_TLS_ERRORS:
                g_value_set_flags (value, priv->tls_errors);
                break;
+       case PROP_PRIORITY:
+               g_value_set_enum (value, priv->priority);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -857,6 +865,22 @@ soup_message_class_init (SoupMessageClass *message_class)
                                    "The verification errors on the message's TLS certificate",
                                    G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
                                    G_PARAM_READWRITE));
+       /**
+        * SOUP_MESSAGE_PRIORITY:
+        *
+        * Sets the priority of the #SoupMessage. See
+        * soup_message_set_priority() for further details.
+        *
+        * Since: 2.44
+        **/
+       g_object_class_install_property (
+               object_class, PROP_PRIORITY,
+               g_param_spec_enum (SOUP_MESSAGE_PRIORITY,
+                                  "Priority",
+                                  "The priority of the message",
+                                  SOUP_TYPE_MESSAGE_PRIORITY,
+                                  SOUP_MESSAGE_PRIORITY_NORMAL,
+                                  G_PARAM_READWRITE));
 }
 
 
@@ -1917,3 +1941,70 @@ soup_message_get_soup_request (SoupMessage *msg)
        return priv->request;
 }
 
+/**
+ * SoupMessagePriority:
+ * @SOUP_MESSAGE_PRIORITY_VERY_LOW: The lowest priority, the messages
+ *   with this priority will be the last ones to be attended.
+ * @SOUP_MESSAGE_PRIORITY_LOW: Use this for low priority messages, a
+ *   #SoupMessage with the default priority will be processed first.
+ * @SOUP_MESSAGE_PRIORITY_NORMAL: The default priotity, this is the
+ *   priority assigned to the #SoupMessage by default.
+ * @SOUP_MESSAGE_PRIORITY_HIGH: High priority, a #SoupMessage with
+ *   this priority will be processed before the ones with the default
+ *   priority.
+ * @SOUP_MESSAGE_PRIORITY_VERY_HIGH: The highest priority, use this
+ *   for very urgent #SoupMessage as they will be the first ones to be
+ *   attended.
+ *
+ * Priorities that can be set on a #SoupMessage to instruct the
+ * message queue to process it before any other message with lower
+ * priority.
+ **/
+
+/**
+ * soup_message_set_priority:
+ * @msg: a #SoupMessage
+ * @priority: the #SoupMessagePriority
+ *
+ * Sets the priority of a message. Note that this won't have any
+ * effect unless used before the message is added to the session's
+ * message processing queue.
+ *
+ * The message will be placed just before any other previously added
+ * message with lower priority (messages with the same priority are
+ * processed on a FIFO basis).
+ *
+ * Setting priorities does not currently work with #SoupSessionSync
+ * (or with synchronous messages on a plain #SoupSession) because in
+ * the synchronous/blocking case, priority ends up being determined
+ * semi-randomly by thread scheduling.
+ *
+ * Since: 2.44
+ */
+void
+soup_message_set_priority (SoupMessage        *msg,
+                          SoupMessagePriority priority)
+{
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       g_object_set (msg, SOUP_MESSAGE_PRIORITY, priority, NULL);
+}
+
+/**
+ * soup_message_get_priority:
+ * @msg: a #SoupMessage
+ *
+ * Retrieves the #SoupMessagePriority. If not set this value defaults
+ * to #SOUP_MESSAGE_PRIORITY_NORMAL.
+ *
+ * Return value: the priority of the message.
+ *
+ * Since: 2.44
+ */
+SoupMessagePriority
+soup_message_get_priority (SoupMessage *msg)
+{
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_MESSAGE_PRIORITY_NORMAL);
+
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->priority;
+}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 23ec024..22c2b1e 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -74,6 +74,7 @@ GType soup_message_get_type (void);
 #define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
 #define SOUP_MESSAGE_TLS_CERTIFICATE  "tls-certificate"
 #define SOUP_MESSAGE_TLS_ERRORS       "tls-errors"
+#define SOUP_MESSAGE_PRIORITY         "priority"
 
 SoupMessage   *soup_message_new                 (const char        *method,
                                                 const char        *uri_string);
@@ -186,6 +187,23 @@ void           soup_message_disable_feature     (SoupMessage       *msg,
 SOUP_AVAILABLE_IN_2_42
 SoupRequest   *soup_message_get_soup_request    (SoupMessage       *msg);
 
+
+typedef enum {
+       SOUP_MESSAGE_PRIORITY_VERY_LOW = 0,
+       SOUP_MESSAGE_PRIORITY_LOW,
+       SOUP_MESSAGE_PRIORITY_NORMAL,
+       SOUP_MESSAGE_PRIORITY_HIGH,
+       SOUP_MESSAGE_PRIORITY_VERY_HIGH
+} SoupMessagePriority;
+
+SOUP_AVAILABLE_IN_2_44
+void                soup_message_set_priority   (SoupMessage        *msg,
+                                                SoupMessagePriority priority);
+
+
+SOUP_AVAILABLE_IN_2_44
+SoupMessagePriority soup_message_get_priority   (SoupMessage        *msg);
+
 void soup_message_wrote_informational (SoupMessage *msg);
 void soup_message_wrote_headers       (SoupMessage *msg);
 void soup_message_wrote_chunk         (SoupMessage *msg);
diff --git a/tests/session-test.c b/tests/session-test.c
index 3207797..b8e224e 100644
--- a/tests/session-test.c
+++ b/tests/session-test.c
@@ -5,6 +5,7 @@
 static gboolean server_processed_message;
 static gboolean timeout;
 static GMainLoop *loop;
+static SoupMessagePriority expected_priorities[3];
 
 static gboolean
 timeout_cb (gpointer user_data)
@@ -207,6 +208,61 @@ do_sync_tests (char *uri, char *timeout_uri)
        soup_test_session_abort_unref (session);
 }
 
+static void
+priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       guint *finished_count = user_data;
+       SoupMessagePriority priority = soup_message_get_priority (msg);
+
+       if (priority != expected_priorities[*finished_count]) {
+               debug_printf (1, "    message %d should have priority %d (%d found)\n",
+                             *finished_count, expected_priorities[*finished_count], priority);
+               errors++;
+       } else
+               debug_printf (1, "  received message %d with priority %d\n",
+                             *finished_count, priority);
+
+       (*finished_count)++;
+}
+
+static void
+do_priority_tests (char *uri)
+{
+       SoupSession *session;
+       int i, finished_count = 0;
+       SoupMessagePriority priorities[] =
+               { SOUP_MESSAGE_PRIORITY_LOW,
+                 SOUP_MESSAGE_PRIORITY_HIGH,
+                 SOUP_MESSAGE_PRIORITY_NORMAL };
+
+       debug_printf (1, "\nSoupSessionAsync\n");
+
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+       g_object_set (session, "max-conns", 1, NULL);
+
+       expected_priorities[0] = SOUP_MESSAGE_PRIORITY_HIGH;
+       expected_priorities[1] = SOUP_MESSAGE_PRIORITY_NORMAL;
+       expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW;
+
+       for (i = 0; i < 3; i++) {
+               char *msg_uri;
+               SoupMessage *msg;
+
+               msg_uri = g_strdup_printf ("%s/%d", uri, i);
+               msg = soup_message_new ("GET", uri);
+               g_free (msg_uri);
+
+               soup_message_set_priority (msg, priorities[i]);
+               soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count);
+       }
+
+       debug_printf (2, "    waiting for finished\n");
+       while (finished_count != 3)
+               g_main_context_iteration (NULL, TRUE);
+
+       soup_test_session_abort_unref (session);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -224,6 +280,7 @@ main (int argc, char **argv)
        do_plain_tests (uri, timeout_uri);
        do_async_tests (uri, timeout_uri);
        do_sync_tests (uri, timeout_uri);
+       do_priority_tests (uri);
 
        g_free (uri);
        g_free (timeout_uri);


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