Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-message-queue.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message-queue.c: Message queue
4  *
5  * Copyright (C) 2003 Novell, Inc.
6  * Copyright (C) 2008 Red Hat, Inc.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include "soup-message-queue.h"
14 #include "soup.h"
15
16 /* This is an internal structure used by #SoupSession and its
17  * subclasses to keep track of the status of messages currently being
18  * processed.
19  *
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.
27  **/
28
29 struct _SoupMessageQueue {
30         SoupSession *session;
31
32         GMutex mutex;
33         SoupMessageQueueItem *head, *tail;
34 };
35
36 SoupMessageQueue *
37 soup_message_queue_new (SoupSession *session)
38 {
39         SoupMessageQueue *queue;
40
41         queue = g_slice_new0 (SoupMessageQueue);
42         queue->session = session;
43         g_mutex_init (&queue->mutex);
44         return queue;
45 }
46
47 void
48 soup_message_queue_destroy (SoupMessageQueue *queue)
49 {
50         g_return_if_fail (queue->head == NULL);
51
52         g_mutex_clear (&queue->mutex);
53         g_slice_free (SoupMessageQueue, queue);
54 }
55
56 static void
57 queue_message_restarted (SoupMessage *msg, gpointer user_data)
58 {
59         SoupMessageQueueItem *item = user_data;
60
61         g_cancellable_reset (item->cancellable);
62 }
63
64 /**
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
70  *
71  * Creates a new #SoupMessageQueueItem and appends it to @queue.
72  *
73  * Return value: the new item, which you must unref with
74  * soup_message_queue_unref_item() when you are done with.
75  **/
76 SoupMessageQueueItem *
77 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
78                            SoupSessionCallback callback, gpointer user_data)
79 {
80         SoupMessageQueueItem *item;
81
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);
87         item->queue = queue;
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);
93
94         g_signal_connect (msg, "restarted",
95                           G_CALLBACK (queue_message_restarted), item);
96
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
99          * "removed" flag.
100          */
101         item->ref_count = 1;
102
103         g_mutex_lock (&queue->mutex);
104         if (queue->head) {
105                 SoupMessageQueueItem *it = queue->head;
106
107                 while (it && it->priority >= item->priority)
108                         it = it->next;
109
110                 if (!it) {
111                         if (queue->tail) {
112                                 queue->tail->next = item;
113                                 item->prev = queue->tail;
114                         } else
115                                 queue->head = item;
116                         queue->tail = item;
117                 } else {
118                         if (it != queue->head)
119                                 it->prev->next = item;
120                         else
121                                 queue->head = item;
122                         item->prev = it->prev;
123                         it->prev = item;
124                         item->next = it;
125                 }
126         } else
127                 queue->head = queue->tail = item;
128
129         g_mutex_unlock (&queue->mutex);
130         return item;
131 }
132
133 /**
134  * soup_message_queue_item_ref:
135  * @item: a #SoupMessageQueueItem
136  *
137  * Refs @item.
138  **/ 
139 void
140 soup_message_queue_item_ref (SoupMessageQueueItem *item)
141 {
142         item->ref_count++;
143 }
144
145 /**
146  * soup_message_queue_item_unref:
147  * @item: a #SoupMessageQueueItem
148  *
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()).
152  **/ 
153 void
154 soup_message_queue_item_unref (SoupMessageQueueItem *item)
155 {
156         g_mutex_lock (&item->queue->mutex);
157
158         /* Decrement the ref_count; if it's still non-zero OR if the
159          * item is still in the queue, then return.
160          */
161         if (--item->ref_count || !item->removed) {
162                 g_mutex_unlock (&item->queue->mutex);
163                 return;
164         }
165
166         g_warn_if_fail (item->conn == NULL);
167
168         /* OK, @item is dead. Rewrite @queue around it */
169         if (item->prev)
170                 item->prev->next = item->next;
171         else
172                 item->queue->head = item->next;
173         if (item->next)
174                 item->next->prev = item->prev;
175         else
176                 item->queue->tail = item->prev;
177
178         g_mutex_unlock (&item->queue->mutex);
179
180         /* And free it */
181         g_signal_handlers_disconnect_by_func (item->msg,
182                                               queue_message_restarted, item);
183         g_object_unref (item->session);
184         g_object_unref (item->msg);
185         g_object_unref (item->cancellable);
186         g_clear_error (&item->error);
187         g_clear_object (&item->task);
188         g_clear_pointer (&item->async_context, g_main_context_unref);
189         if (item->io_source) {
190                 g_source_destroy (item->io_source);
191                 g_source_unref (item->io_source);
192         }
193         g_slice_free (SoupMessageQueueItem, item);
194 }
195
196 /**
197  * soup_message_queue_lookup:
198  * @queue: a #SoupMessageQueue
199  * @msg: a #SoupMessage
200  *
201  * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
202  * the item with soup_message_queue_unref_item() when you are done
203  * with it.
204  *
205  * Return value: the queue item for @msg, or %NULL
206  **/ 
207 SoupMessageQueueItem *
208 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
209 {
210         SoupMessageQueueItem *item;
211
212         g_mutex_lock (&queue->mutex);
213
214         item = queue->tail;
215         while (item && (item->removed || item->msg != msg))
216                 item = item->prev;
217
218         if (item)
219                 item->ref_count++;
220
221         g_mutex_unlock (&queue->mutex);
222         return item;
223 }
224
225 /**
226  * soup_message_queue_first:
227  * @queue: a #SoupMessageQueue
228  *
229  * Gets the first item in @queue. You must unref the item by calling
230  * soup_message_queue_unref_item() on it when you are done.
231  * (soup_message_queue_next() does this for you automatically, so you
232  * only need to unref the item yourself if you are not going to
233  * finishing walking the queue.)
234  *
235  * Return value: the first item in @queue.
236  **/ 
237 SoupMessageQueueItem *
238 soup_message_queue_first (SoupMessageQueue *queue)
239 {
240         SoupMessageQueueItem *item;
241
242         g_mutex_lock (&queue->mutex);
243
244         item = queue->head;
245         while (item && item->removed)
246                 item = item->next;
247
248         if (item)
249                 item->ref_count++;
250
251         g_mutex_unlock (&queue->mutex);
252         return item;
253 }
254
255 /**
256  * soup_message_queue_next:
257  * @queue: a #SoupMessageQueue
258  * @item: a #SoupMessageQueueItem
259  *
260  * Unrefs @item and gets the next item after it in @queue. As with
261  * soup_message_queue_first(), you must unref the returned item
262  * yourself with soup_message_queue_unref_item() if you do not finish
263  * walking the queue.
264  *
265  * Return value: the next item in @queue.
266  **/ 
267 SoupMessageQueueItem *
268 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
269 {
270         SoupMessageQueueItem *next;
271
272         g_mutex_lock (&queue->mutex);
273
274         next = item->next;
275         while (next && next->removed)
276                 next = next->next;
277         if (next)
278                 next->ref_count++;
279
280         g_mutex_unlock (&queue->mutex);
281         soup_message_queue_item_unref (item);
282         return next;
283 }
284
285 /**
286  * soup_message_queue_remove:
287  * @queue: a #SoupMessageQueue
288  * @item: a #SoupMessageQueueItem
289  *
290  * Removes @item from @queue. Note that you probably also need to call
291  * soup_message_queue_unref_item() after this.
292  **/ 
293 void
294 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
295 {
296         g_return_if_fail (!item->removed);
297
298         g_mutex_lock (&queue->mutex);
299         item->removed = TRUE;
300         g_mutex_unlock (&queue->mutex);
301 }