Git init
[profile/ivi/libsoup2.4.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-uri.h"
15
16 /**
17  * SECTION:soup-message-queue
18  *
19  * This is an internal structure used by #SoupSession and its
20  * subclasses to keep track of the status of messages currently being
21  * processed.
22  *
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.
30  **/
31
32 struct _SoupMessageQueue {
33         SoupSession *session;
34
35         GMutex *mutex;
36         SoupMessageQueueItem *head, *tail;
37 };
38
39 SoupMessageQueue *
40 soup_message_queue_new (SoupSession *session)
41 {
42         SoupMessageQueue *queue;
43
44         queue = g_slice_new0 (SoupMessageQueue);
45         queue->session = session;
46         queue->mutex = g_mutex_new ();
47         return queue;
48 }
49
50 void
51 soup_message_queue_destroy (SoupMessageQueue *queue)
52 {
53         g_return_if_fail (queue->head == NULL);
54
55         g_mutex_free (queue->mutex);
56         g_slice_free (SoupMessageQueue, queue);
57 }
58
59 static void
60 queue_message_restarted (SoupMessage *msg, gpointer user_data)
61 {
62         SoupMessageQueueItem *item = user_data;
63
64         if (item->proxy_addr) {
65                 g_object_unref (item->proxy_addr);
66                 item->proxy_addr = NULL;
67         }
68         if (item->proxy_uri) {
69                 soup_uri_free (item->proxy_uri);
70                 item->proxy_uri = NULL;
71         }
72
73         if (item->conn &&
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);
79                 item->conn = NULL;
80         }
81
82         soup_message_cleanup_response (msg);
83
84         g_cancellable_reset (item->cancellable);
85
86         item->state = SOUP_MESSAGE_STARTING;
87 }
88
89 /**
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
95  *
96  * Creates a new #SoupMessageQueueItem and appends it to @queue.
97  *
98  * Return value: the new item, which you must unref with
99  * soup_message_queue_unref_item() when you are done with.
100  **/
101 SoupMessageQueueItem *
102 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
103                            SoupSessionCallback callback, gpointer user_data)
104 {
105         SoupMessageQueueItem *item;
106
107         item = g_slice_new0 (SoupMessageQueueItem);
108         item->session = queue->session;
109         item->queue = queue;
110         item->msg = g_object_ref (msg);
111         item->callback = callback;
112         item->callback_data = user_data;
113         item->cancellable = g_cancellable_new ();
114
115         g_signal_connect (msg, "restarted",
116                           G_CALLBACK (queue_message_restarted), item);
117
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
120          * "removed" flag.
121          */
122         item->ref_count = 1;
123
124         g_mutex_lock (queue->mutex);
125         if (queue->head) {
126                 queue->tail->next = item;
127                 item->prev = queue->tail;
128                 queue->tail = item;
129         } else
130                 queue->head = queue->tail = item;
131
132         g_mutex_unlock (queue->mutex);
133         return item;
134 }
135
136 /**
137  * soup_message_queue_item_ref:
138  * @item: a #SoupMessageQueueItem
139  *
140  * Refs @item.
141  **/ 
142 void
143 soup_message_queue_item_ref (SoupMessageQueueItem *item)
144 {
145         item->ref_count++;
146 }
147
148 /**
149  * soup_message_queue_item_unref:
150  * @item: a #SoupMessageQueueItem
151  *
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()).
155  **/ 
156 void
157 soup_message_queue_item_unref (SoupMessageQueueItem *item)
158 {
159         g_mutex_lock (item->queue->mutex);
160
161         /* Decrement the ref_count; if it's still non-zero OR if the
162          * item is still in the queue, then return.
163          */
164         if (--item->ref_count || !item->removed) {
165                 g_mutex_unlock (item->queue->mutex);
166                 return;
167         }
168
169         /* OK, @item is dead. Rewrite @queue around it */
170         if (item->prev)
171                 item->prev->next = item->next;
172         else
173                 item->queue->head = item->next;
174         if (item->next)
175                 item->next->prev = item->prev;
176         else
177                 item->queue->tail = item->prev;
178
179         g_mutex_unlock (item->queue->mutex);
180
181         /* And free it */
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);
188         if (item->proxy_uri)
189                 soup_uri_free (item->proxy_uri);
190         if (item->conn)
191                 g_object_unref (item->conn);
192         g_slice_free (SoupMessageQueueItem, item);
193 }
194
195 /**
196  * soup_message_queue_lookup:
197  * @queue: a #SoupMessageQueue
198  * @msg: a #SoupMessage
199  *
200  * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
201  * the item with soup_message_queue_unref_item() when you are done
202  * with it.
203  *
204  * Return value: the queue item for @msg, or %NULL
205  **/ 
206 SoupMessageQueueItem *
207 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
208 {
209         SoupMessageQueueItem *item;
210
211         g_mutex_lock (queue->mutex);
212
213         item = queue->tail;
214         while (item && (item->removed || item->msg != msg))
215                 item = item->prev;
216
217         if (item)
218                 item->ref_count++;
219
220         g_mutex_unlock (queue->mutex);
221         return item;
222 }
223
224 /**
225  * soup_message_queue_first:
226  * @queue: a #SoupMessageQueue
227  *
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.)
233  *
234  * Return value: the first item in @queue.
235  **/ 
236 SoupMessageQueueItem *
237 soup_message_queue_first (SoupMessageQueue *queue)
238 {
239         SoupMessageQueueItem *item;
240
241         g_mutex_lock (queue->mutex);
242
243         item = queue->head;
244         while (item && item->removed)
245                 item = item->next;
246
247         if (item)
248                 item->ref_count++;
249
250         g_mutex_unlock (queue->mutex);
251         return item;
252 }
253
254 /**
255  * soup_message_queue_next:
256  * @queue: a #SoupMessageQueue
257  * @item: a #SoupMessageQueueItem
258  *
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
262  * walking the queue.
263  *
264  * Return value: the next item in @queue.
265  **/ 
266 SoupMessageQueueItem *
267 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
268 {
269         SoupMessageQueueItem *next;
270
271         g_mutex_lock (queue->mutex);
272
273         next = item->next;
274         while (next && next->removed)
275                 next = next->next;
276         if (next)
277                 next->ref_count++;
278
279         g_mutex_unlock (queue->mutex);
280         soup_message_queue_item_unref (item);
281         return next;
282 }
283
284 /**
285  * soup_message_queue_remove:
286  * @queue: a #SoupMessageQueue
287  * @item: a #SoupMessageQueueItem
288  *
289  * Removes @item from @queue. Note that you probably also need to call
290  * soup_message_queue_unref_item() after this.
291  **/ 
292 void
293 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
294 {
295         g_return_if_fail (!item->removed);
296
297         g_mutex_lock (queue->mutex);
298         item->removed = TRUE;
299         g_mutex_unlock (queue->mutex);
300 }