1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message-queue.c: Message queue
5 * Copyright (C) 2003 Novell, Inc.
6 * Copyright (C) 2008 Red Hat, Inc.
13 #include "soup-message-queue.h"
17 * SECTION:soup-message-queue
19 * This is an internal structure used by #SoupSession and its
20 * subclasses to keep track of the status of messages currently being
23 * The #SoupMessageQueue itself is mostly just a linked list of
24 * #SoupMessageQueueItem, with some added cleverness to allow the list
25 * to be walked safely while other threads / re-entrant loops are
26 * adding items to and removing items from it. In particular, this is
27 * handled by refcounting items and then keeping "removed" items in
28 * the list until their ref_count drops to 0, but skipping over the
29 * "removed" ones when walking the queue.
32 struct _SoupMessageQueue {
36 SoupMessageQueueItem *head, *tail;
40 soup_message_queue_new (SoupSession *session)
42 SoupMessageQueue *queue;
44 queue = g_slice_new0 (SoupMessageQueue);
45 queue->session = session;
46 queue->mutex = g_mutex_new ();
51 soup_message_queue_destroy (SoupMessageQueue *queue)
53 g_return_if_fail (queue->head == NULL);
55 g_mutex_free (queue->mutex);
56 g_slice_free (SoupMessageQueue, queue);
60 queue_message_restarted (SoupMessage *msg, gpointer user_data)
62 SoupMessageQueueItem *item = user_data;
64 if (item->proxy_addr) {
65 g_object_unref (item->proxy_addr);
66 item->proxy_addr = NULL;
68 if (item->proxy_uri) {
69 soup_uri_free (item->proxy_uri);
70 item->proxy_uri = NULL;
74 (!soup_message_is_keepalive (msg) ||
75 SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
76 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
77 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
78 g_object_unref (item->conn);
82 soup_message_cleanup_response (msg);
84 g_cancellable_reset (item->cancellable);
86 item->state = SOUP_MESSAGE_STARTING;
90 * soup_message_queue_append:
91 * @queue: a #SoupMessageQueue
92 * @msg: a #SoupMessage
93 * @callback: the callback for @msg
94 * @user_data: the data to pass to @callback
96 * Creates a new #SoupMessageQueueItem and appends it to @queue.
98 * Return value: the new item, which you must unref with
99 * soup_message_queue_unref_item() when you are done with.
101 SoupMessageQueueItem *
102 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
103 SoupSessionCallback callback, gpointer user_data)
105 SoupMessageQueueItem *item;
107 item = g_slice_new0 (SoupMessageQueueItem);
108 item->session = queue->session;
110 item->msg = g_object_ref (msg);
111 item->callback = callback;
112 item->callback_data = user_data;
113 item->cancellable = g_cancellable_new ();
115 g_signal_connect (msg, "restarted",
116 G_CALLBACK (queue_message_restarted), item);
118 /* Note: the initial ref_count of 1 represents the caller's
119 * ref; the queue's own ref is indicated by the absence of the
124 g_mutex_lock (queue->mutex);
126 queue->tail->next = item;
127 item->prev = queue->tail;
130 queue->head = queue->tail = item;
132 g_mutex_unlock (queue->mutex);
137 * soup_message_queue_item_ref:
138 * @item: a #SoupMessageQueueItem
143 soup_message_queue_item_ref (SoupMessageQueueItem *item)
149 * soup_message_queue_item_unref:
150 * @item: a #SoupMessageQueueItem
152 * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
153 * with (but that you aren't passing to
154 * soup_message_queue_item_next()).
157 soup_message_queue_item_unref (SoupMessageQueueItem *item)
159 g_mutex_lock (item->queue->mutex);
161 /* Decrement the ref_count; if it's still non-zero OR if the
162 * item is still in the queue, then return.
164 if (--item->ref_count || !item->removed) {
165 g_mutex_unlock (item->queue->mutex);
169 /* OK, @item is dead. Rewrite @queue around it */
171 item->prev->next = item->next;
173 item->queue->head = item->next;
175 item->next->prev = item->prev;
177 item->queue->tail = item->prev;
179 g_mutex_unlock (item->queue->mutex);
182 g_signal_handlers_disconnect_by_func (item->msg,
183 queue_message_restarted, item);
184 g_object_unref (item->msg);
185 g_object_unref (item->cancellable);
186 if (item->proxy_addr)
187 g_object_unref (item->proxy_addr);
189 soup_uri_free (item->proxy_uri);
191 g_object_unref (item->conn);
192 g_slice_free (SoupMessageQueueItem, item);
196 * soup_message_queue_lookup:
197 * @queue: a #SoupMessageQueue
198 * @msg: a #SoupMessage
200 * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
201 * the item with soup_message_queue_unref_item() when you are done
204 * Return value: the queue item for @msg, or %NULL
206 SoupMessageQueueItem *
207 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
209 SoupMessageQueueItem *item;
211 g_mutex_lock (queue->mutex);
214 while (item && (item->removed || item->msg != msg))
220 g_mutex_unlock (queue->mutex);
225 * soup_message_queue_first:
226 * @queue: a #SoupMessageQueue
228 * Gets the first item in @queue. You must unref the item by calling
229 * soup_message_queue_unref_item() on it when you are done.
230 * (soup_message_queue_next() does this for you automatically, so you
231 * only need to unref the item yourself if you are not going to
232 * finishing walking the queue.)
234 * Return value: the first item in @queue.
236 SoupMessageQueueItem *
237 soup_message_queue_first (SoupMessageQueue *queue)
239 SoupMessageQueueItem *item;
241 g_mutex_lock (queue->mutex);
244 while (item && item->removed)
250 g_mutex_unlock (queue->mutex);
255 * soup_message_queue_next:
256 * @queue: a #SoupMessageQueue
257 * @item: a #SoupMessageQueueItem
259 * Unrefs @item and gets the next item after it in @queue. As with
260 * soup_message_queue_first(), you must unref the returned item
261 * yourself with soup_message_queue_unref_item() if you do not finish
264 * Return value: the next item in @queue.
266 SoupMessageQueueItem *
267 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
269 SoupMessageQueueItem *next;
271 g_mutex_lock (queue->mutex);
274 while (next && next->removed)
279 g_mutex_unlock (queue->mutex);
280 soup_message_queue_item_unref (item);
285 * soup_message_queue_remove:
286 * @queue: a #SoupMessageQueue
287 * @item: a #SoupMessageQueueItem
289 * Removes @item from @queue. Note that you probably also need to call
290 * soup_message_queue_unref_item() after this.
293 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
295 g_return_if_fail (!item->removed);
297 g_mutex_lock (queue->mutex);
298 item->removed = TRUE;
299 g_mutex_unlock (queue->mutex);