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 g_cancellable_reset (item->cancellable);
65 * soup_message_queue_append:
66 * @queue: a #SoupMessageQueue
67 * @msg: a #SoupMessage
68 * @callback: the callback for @msg
69 * @user_data: the data to pass to @callback
71 * Creates a new #SoupMessageQueueItem and appends it to @queue.
73 * Return value: the new item, which you must unref with
74 * soup_message_queue_unref_item() when you are done with.
76 SoupMessageQueueItem *
77 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
78 SoupSessionCallback callback, gpointer user_data)
80 SoupMessageQueueItem *item;
82 item = g_slice_new0 (SoupMessageQueueItem);
83 item->session = g_object_ref (queue->session);
84 item->async_context = soup_session_get_async_context (item->session);
85 if (item->async_context)
86 g_main_context_ref (item->async_context);
88 item->msg = g_object_ref (msg);
89 item->callback = callback;
90 item->callback_data = user_data;
91 item->cancellable = g_cancellable_new ();
92 item->priority = soup_message_get_priority (msg);
94 g_signal_connect (msg, "restarted",
95 G_CALLBACK (queue_message_restarted), item);
97 /* Note: the initial ref_count of 1 represents the caller's
98 * ref; the queue's own ref is indicated by the absence of the
103 g_mutex_lock (&queue->mutex);
105 SoupMessageQueueItem *it = queue->head;
107 while (it && it->priority >= item->priority)
112 queue->tail->next = item;
113 item->prev = queue->tail;
118 if (it != queue->head)
119 it->prev->next = item;
122 item->prev = it->prev;
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)
142 g_mutex_lock (&item->queue->mutex);
144 g_mutex_unlock (&item->queue->mutex);
148 * soup_message_queue_item_unref:
149 * @item: a #SoupMessageQueueItem
151 * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
152 * with (but that you aren't passing to
153 * soup_message_queue_item_next()).
156 soup_message_queue_item_unref (SoupMessageQueueItem *item)
158 g_mutex_lock (&item->queue->mutex);
160 /* Decrement the ref_count; if it's still non-zero OR if the
161 * item is still in the queue, then return.
163 if (--item->ref_count || !item->removed) {
164 g_mutex_unlock (&item->queue->mutex);
168 g_warn_if_fail (item->conn == NULL);
170 /* OK, @item is dead. Rewrite @queue around it */
172 item->prev->next = item->next;
174 item->queue->head = item->next;
176 item->next->prev = item->prev;
178 item->queue->tail = item->prev;
180 g_mutex_unlock (&item->queue->mutex);
183 g_signal_handlers_disconnect_by_func (item->msg,
184 queue_message_restarted, item);
185 g_object_unref (item->session);
186 g_object_unref (item->msg);
187 g_object_unref (item->cancellable);
188 g_clear_error (&item->error);
189 g_clear_object (&item->task);
190 g_clear_pointer (&item->async_context, g_main_context_unref);
191 if (item->io_source) {
192 g_source_destroy (item->io_source);
193 g_source_unref (item->io_source);
195 g_slice_free (SoupMessageQueueItem, item);
199 * soup_message_queue_lookup:
200 * @queue: a #SoupMessageQueue
201 * @msg: a #SoupMessage
203 * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
204 * the item with soup_message_queue_unref_item() when you are done
207 * Return value: (nullable): the queue item for @msg, or %NULL
209 SoupMessageQueueItem *
210 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
212 SoupMessageQueueItem *item;
214 g_mutex_lock (&queue->mutex);
217 while (item && (item->removed || item->msg != msg))
223 g_mutex_unlock (&queue->mutex);
228 * soup_message_queue_first:
229 * @queue: a #SoupMessageQueue
231 * Gets the first item in @queue. You must unref the item by calling
232 * soup_message_queue_unref_item() on it when you are done.
233 * (soup_message_queue_next() does this for you automatically, so you
234 * only need to unref the item yourself if you are not going to
235 * finishing walking the queue.)
237 * Return value: the first item in @queue.
239 SoupMessageQueueItem *
240 soup_message_queue_first (SoupMessageQueue *queue)
242 SoupMessageQueueItem *item;
244 g_mutex_lock (&queue->mutex);
247 while (item && item->removed)
253 g_mutex_unlock (&queue->mutex);
258 * soup_message_queue_next:
259 * @queue: a #SoupMessageQueue
260 * @item: a #SoupMessageQueueItem
262 * Unrefs @item and gets the next item after it in @queue. As with
263 * soup_message_queue_first(), you must unref the returned item
264 * yourself with soup_message_queue_unref_item() if you do not finish
267 * Return value: the next item in @queue.
269 SoupMessageQueueItem *
270 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
272 SoupMessageQueueItem *next;
274 g_mutex_lock (&queue->mutex);
277 while (next && next->removed)
282 g_mutex_unlock (&queue->mutex);
283 soup_message_queue_item_unref (item);
288 * soup_message_queue_remove:
289 * @queue: a #SoupMessageQueue
290 * @item: a #SoupMessageQueueItem
292 * Removes @item from @queue. Note that you probably also need to call
293 * soup_message_queue_unref_item() after this.
296 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
298 g_return_if_fail (!item->removed);
300 g_mutex_lock (&queue->mutex);
301 item->removed = TRUE;
302 g_mutex_unlock (&queue->mutex);