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"
16 /* This is an internal structure used by #SoupSession and its
17 * subclasses to keep track of the status of messages currently being
20 * The #SoupMessageQueue itself is mostly just a linked list of
21 * #SoupMessageQueueItem, with some added cleverness to allow the list
22 * to be walked safely while other threads / re-entrant loops are
23 * adding items to and removing items from it. In particular, this is
24 * handled by refcounting items and then keeping "removed" items in
25 * the list until their ref_count drops to 0, but skipping over the
26 * "removed" ones when walking the queue.
29 struct _SoupMessageQueue {
33 SoupMessageQueueItem *head, *tail;
37 soup_message_queue_new (SoupSession *session)
39 SoupMessageQueue *queue;
41 queue = g_slice_new0 (SoupMessageQueue);
42 queue->session = session;
43 g_mutex_init (&queue->mutex);
48 soup_message_queue_destroy (SoupMessageQueue *queue)
50 g_return_if_fail (queue->head == NULL);
52 g_mutex_clear (&queue->mutex);
53 g_slice_free (SoupMessageQueue, queue);
57 queue_message_restarted (SoupMessage *msg, gpointer user_data)
59 SoupMessageQueueItem *item = user_data;
61 if (item->proxy_addr) {
62 g_object_unref (item->proxy_addr);
63 item->proxy_addr = NULL;
65 if (item->proxy_uri) {
66 soup_uri_free (item->proxy_uri);
67 item->proxy_uri = NULL;
71 (!soup_message_is_keepalive (msg) ||
72 SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
73 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
74 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
75 soup_message_queue_item_set_connection (item, NULL);
78 soup_message_cleanup_response (msg);
80 g_cancellable_reset (item->cancellable);
82 item->state = SOUP_MESSAGE_STARTING;
86 * soup_message_queue_append:
87 * @queue: a #SoupMessageQueue
88 * @msg: a #SoupMessage
89 * @callback: the callback for @msg
90 * @user_data: the data to pass to @callback
92 * Creates a new #SoupMessageQueueItem and appends it to @queue.
94 * Return value: the new item, which you must unref with
95 * soup_message_queue_unref_item() when you are done with.
97 SoupMessageQueueItem *
98 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
99 SoupSessionCallback callback, gpointer user_data)
101 SoupMessageQueueItem *item;
103 item = g_slice_new0 (SoupMessageQueueItem);
104 item->session = queue->session;
105 item->async_context = soup_session_get_async_context (item->session);
107 item->msg = g_object_ref (msg);
108 item->callback = callback;
109 item->callback_data = user_data;
110 item->cancellable = g_cancellable_new ();
112 g_signal_connect (msg, "restarted",
113 G_CALLBACK (queue_message_restarted), item);
115 /* Note: the initial ref_count of 1 represents the caller's
116 * ref; the queue's own ref is indicated by the absence of the
121 g_mutex_lock (&queue->mutex);
123 queue->tail->next = item;
124 item->prev = queue->tail;
127 queue->head = queue->tail = item;
129 g_mutex_unlock (&queue->mutex);
134 * soup_message_queue_item_ref:
135 * @item: a #SoupMessageQueueItem
140 soup_message_queue_item_ref (SoupMessageQueueItem *item)
146 * soup_message_queue_item_unref:
147 * @item: a #SoupMessageQueueItem
149 * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
150 * with (but that you aren't passing to
151 * soup_message_queue_item_next()).
154 soup_message_queue_item_unref (SoupMessageQueueItem *item)
156 g_mutex_lock (&item->queue->mutex);
158 /* Decrement the ref_count; if it's still non-zero OR if the
159 * item is still in the queue, then return.
161 if (--item->ref_count || !item->removed) {
162 g_mutex_unlock (&item->queue->mutex);
166 /* OK, @item is dead. Rewrite @queue around it */
168 item->prev->next = item->next;
170 item->queue->head = item->next;
172 item->next->prev = item->prev;
174 item->queue->tail = item->prev;
176 g_mutex_unlock (&item->queue->mutex);
179 g_signal_handlers_disconnect_by_func (item->msg,
180 queue_message_restarted, item);
181 g_object_unref (item->msg);
182 g_object_unref (item->cancellable);
183 if (item->proxy_addr)
184 g_object_unref (item->proxy_addr);
186 soup_uri_free (item->proxy_uri);
188 g_object_unref (item->result);
189 soup_message_queue_item_set_connection (item, NULL);
190 g_slice_free (SoupMessageQueueItem, item);
194 proxy_connection_event (SoupConnection *conn,
195 GSocketClientEvent event,
196 GIOStream *connection,
199 SoupMessageQueueItem *item = user_data;
201 soup_message_network_event (item->msg, event, connection);
205 soup_message_queue_item_set_connection (SoupMessageQueueItem *item,
206 SoupConnection *conn)
209 g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item);
210 g_object_unref (item->conn);
216 g_object_ref (item->conn);
217 g_signal_connect (item->conn, "event",
218 G_CALLBACK (proxy_connection_event), item);
223 * soup_message_queue_lookup:
224 * @queue: a #SoupMessageQueue
225 * @msg: a #SoupMessage
227 * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
228 * the item with soup_message_queue_unref_item() when you are done
231 * Return value: the queue item for @msg, or %NULL
233 SoupMessageQueueItem *
234 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
236 SoupMessageQueueItem *item;
238 g_mutex_lock (&queue->mutex);
241 while (item && (item->removed || item->msg != msg))
247 g_mutex_unlock (&queue->mutex);
252 * soup_message_queue_first:
253 * @queue: a #SoupMessageQueue
255 * Gets the first item in @queue. You must unref the item by calling
256 * soup_message_queue_unref_item() on it when you are done.
257 * (soup_message_queue_next() does this for you automatically, so you
258 * only need to unref the item yourself if you are not going to
259 * finishing walking the queue.)
261 * Return value: the first item in @queue.
263 SoupMessageQueueItem *
264 soup_message_queue_first (SoupMessageQueue *queue)
266 SoupMessageQueueItem *item;
268 g_mutex_lock (&queue->mutex);
271 while (item && item->removed)
277 g_mutex_unlock (&queue->mutex);
282 * soup_message_queue_next:
283 * @queue: a #SoupMessageQueue
284 * @item: a #SoupMessageQueueItem
286 * Unrefs @item and gets the next item after it in @queue. As with
287 * soup_message_queue_first(), you must unref the returned item
288 * yourself with soup_message_queue_unref_item() if you do not finish
291 * Return value: the next item in @queue.
293 SoupMessageQueueItem *
294 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
296 SoupMessageQueueItem *next;
298 g_mutex_lock (&queue->mutex);
301 while (next && next->removed)
306 g_mutex_unlock (&queue->mutex);
307 soup_message_queue_item_unref (item);
312 * soup_message_queue_remove:
313 * @queue: a #SoupMessageQueue
314 * @item: a #SoupMessageQueueItem
316 * Removes @item from @queue. Note that you probably also need to call
317 * soup_message_queue_unref_item() after this.
320 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
322 g_return_if_fail (!item->removed);
324 g_mutex_lock (&queue->mutex);
325 item->removed = TRUE;
326 g_mutex_unlock (&queue->mutex);