Change the SoupURI properties to SoupAddress properties.
[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
15 /**
16  * SECTION:soup-message-queue
17  *
18  * This is an internal structure used by #SoupSession and its
19  * subclasses to keep track of the status of messages currently being
20  * processed.
21  *
22  * The #SoupMessageQueue itself is mostly just a linked list of
23  * #SoupMessageQueueItem, with some added cleverness to allow the list
24  * to be walked safely while other threads / re-entrant loops are
25  * adding items to and removing items from it. In particular, this is
26  * handled by refcounting items and then keeping "removed" items in
27  * the list until their ref_count drops to 0, but skipping over the
28  * "removed" ones when walking the queue.
29  **/
30
31 struct SoupMessageQueue {
32         SoupSession *session;
33
34         GMutex *mutex;
35         SoupMessageQueueItem *head, *tail;
36 };
37
38 SoupMessageQueue *
39 soup_message_queue_new (SoupSession *session)
40 {
41         SoupMessageQueue *queue;
42
43         queue = g_slice_new0 (SoupMessageQueue);
44         queue->session = session;
45         queue->mutex = g_mutex_new ();
46         return queue;
47 }
48
49 void
50 soup_message_queue_destroy (SoupMessageQueue *queue)
51 {
52         g_return_if_fail (queue->head == NULL);
53
54         g_mutex_free (queue->mutex);
55         g_slice_free (SoupMessageQueue, queue);
56 }
57
58 /**
59  * soup_message_queue_append:
60  * @queue: a #SoupMessageQueue
61  * @msg: a #SoupMessage
62  * @callback: the callback for @msg
63  * @user_data: the data to pass to @callback
64  *
65  * Creates a new #SoupMessageQueueItem and appends it to @queue.
66  *
67  * Return value: the new item, which you must unref with
68  * soup_message_queue_unref_item() when you are done with.
69  **/
70 SoupMessageQueueItem *
71 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
72                            SoupSessionCallback callback, gpointer user_data)
73 {
74         SoupMessageQueueItem *item;
75
76         item = g_slice_new0 (SoupMessageQueueItem);
77         item->session = queue->session;
78         item->queue = queue;
79         item->msg = g_object_ref (msg);
80         item->callback = callback;
81         item->callback_data = user_data;
82         item->cancellable = g_cancellable_new ();
83
84         /* Note: the initial ref_count of 1 represents the caller's
85          * ref; the queue's own ref is indicated by the absence of the
86          * "removed" flag.
87          */
88         item->ref_count = 1;
89
90         g_mutex_lock (queue->mutex);
91         if (queue->head) {
92                 queue->tail->next = item;
93                 item->prev = queue->tail;
94                 queue->tail = item;
95         } else
96                 queue->head = queue->tail = item;
97
98         g_mutex_unlock (queue->mutex);
99         return item;
100 }
101
102 /**
103  * soup_message_queue_item_ref:
104  * @item: a #SoupMessageQueueItem
105  *
106  * Refs @item.
107  **/ 
108 void
109 soup_message_queue_item_ref (SoupMessageQueueItem *item)
110 {
111         item->ref_count++;
112 }
113
114 /**
115  * soup_message_queue_item_unref:
116  * @item: a #SoupMessageQueueItem
117  *
118  * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
119  * with (but that you aren't passing to
120  * soup_message_queue_item_next()).
121  **/ 
122 void
123 soup_message_queue_item_unref (SoupMessageQueueItem *item)
124 {
125         g_mutex_lock (item->queue->mutex);
126
127         /* Decrement the ref_count; if it's still non-zero OR if the
128          * item is still in the queue, then return.
129          */
130         if (--item->ref_count || !item->removed) {
131                 g_mutex_unlock (item->queue->mutex);
132                 return;
133         }
134
135         /* OK, @item is dead. Rewrite @queue around it */
136         if (item->prev)
137                 item->prev->next = item->next;
138         else
139                 item->queue->head = item->next;
140         if (item->next)
141                 item->next->prev = item->prev;
142         else
143                 item->queue->tail = item->prev;
144
145         g_mutex_unlock (item->queue->mutex);
146
147         /* And free it */
148         g_object_unref (item->msg);
149         g_object_unref (item->cancellable);
150         if (item->msg_addr)
151                 g_object_unref (item->msg_addr);
152         g_slice_free (SoupMessageQueueItem, item);
153 }
154
155 /**
156  * soup_message_queue_lookup:
157  * @queue: a #SoupMessageQueue
158  * @msg: a #SoupMessage
159  *
160  * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
161  * the item with soup_message_queue_unref_item() when you are done
162  * with it.
163  *
164  * Return value: the queue item for @msg, or %NULL
165  **/ 
166 SoupMessageQueueItem *
167 soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
168 {
169         SoupMessageQueueItem *item;
170
171         g_mutex_lock (queue->mutex);
172
173         item = queue->tail;
174         while (item && (item->removed || item->msg != msg))
175                 item = item->prev;
176
177         if (item)
178                 item->ref_count++;
179
180         g_mutex_unlock (queue->mutex);
181         return item;
182 }
183
184 /**
185  * soup_message_queue_first:
186  * @queue: a #SoupMessageQueue
187  *
188  * Gets the first item in @queue. You must unref the item by calling
189  * soup_message_queue_unref_item() on it when you are done.
190  * (soup_message_queue_next() does this for you automatically, so you
191  * only need to unref the item yourself if you are not going to
192  * finishing walking the queue.)
193  *
194  * Return value: the first item in @queue.
195  **/ 
196 SoupMessageQueueItem *
197 soup_message_queue_first (SoupMessageQueue *queue)
198 {
199         SoupMessageQueueItem *item;
200
201         g_mutex_lock (queue->mutex);
202
203         item = queue->head;
204         while (item && item->removed)
205                 item = item->next;
206
207         if (item)
208                 item->ref_count++;
209
210         g_mutex_unlock (queue->mutex);
211         return item;
212 }
213
214 /**
215  * soup_message_queue_next:
216  * @queue: a #SoupMessageQueue
217  * @item: a #SoupMessageQueueItem
218  *
219  * Unrefs @item and gets the next item after it in @queue. As with
220  * soup_message_queue_first(), you must unref the returned item
221  * yourself with soup_message_queue_unref_item() if you do not finish
222  * walking the queue.
223  *
224  * Return value: the next item in @queue.
225  **/ 
226 SoupMessageQueueItem *
227 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
228 {
229         SoupMessageQueueItem *next;
230
231         g_mutex_lock (queue->mutex);
232
233         next = item->next;
234         while (next && next->removed)
235                 next = next->next;
236         if (next)
237                 next->ref_count++;
238
239         g_mutex_unlock (queue->mutex);
240         soup_message_queue_item_unref (item);
241         return next;
242 }
243
244 /**
245  * soup_message_queue_remove:
246  * @queue: a #SoupMessageQueue
247  * @item: a #SoupMessageQueueItem
248  *
249  * Removes @item from @queue. Note that you probably also need to call
250  * soup_message_queue_unref_item() after this.
251  **/ 
252 void
253 soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
254 {
255         g_mutex_lock (queue->mutex);
256         item->removed = TRUE;
257         g_mutex_unlock (queue->mutex);
258 }