Lazy creation of GCond. Only signal GCond, if threads are waiting.
[platform/upstream/glib.git] / glib / gasyncqueue.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GAsyncQueue: asynchronous queue implementation, based on Gqueue.
5  * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * MT safe
25  */
26
27 #include "config.h"
28
29 #include "glib.h"
30
31
32 struct _GAsyncQueue
33 {
34   GMutex *mutex;
35   GCond *cond;
36   GQueue *queue;
37   guint waiting_threads;
38   guint ref_count;
39 };
40
41 /**
42  * g_async_queue_new:
43  * 
44  * Creates a new asynchronous queue with the initial reference count of 1.
45  * 
46  * Return value: the new #GAsyncQueue.
47  **/
48 GAsyncQueue*
49 g_async_queue_new ()
50 {
51   GAsyncQueue* retval = g_new (GAsyncQueue, 1);
52   retval->mutex = g_mutex_new ();
53   retval->cond = NULL;
54   retval->queue = g_queue_new ();
55   retval->waiting_threads = 0;
56   retval->ref_count = 1;
57   return retval;
58 }
59
60 /**
61  * g_async_queue_ref:
62  * @queue: a #GAsyncQueue.
63  *
64  * Increases the reference count of the asynchronous @queue by 1.
65  **/
66 void 
67 g_async_queue_ref (GAsyncQueue *queue)
68 {
69   g_return_if_fail (queue);
70   g_return_if_fail (queue->ref_count > 0);
71   
72   g_mutex_lock (queue->mutex);
73   queue->ref_count++;
74   g_mutex_unlock (queue->mutex);
75 }
76
77 /**
78  * g_async_queue_ref_unlocked:
79  * @queue: a #GAsyncQueue.
80  * 
81  * Increases the reference count of the asynchronous @queue by 1. This
82  * function must be called while holding the @queue's lock.
83  **/
84 void 
85 g_async_queue_ref_unlocked (GAsyncQueue *queue)
86 {
87   g_return_if_fail (queue);
88   g_return_if_fail (queue->ref_count > 0);
89   
90   queue->ref_count++;
91 }
92
93 /**
94  * g_async_queue_unref_and_unlock:
95  * @queue: a #GAsyncQueue.
96  * 
97  * Decreases the reference count of the asynchronous @queue by 1 and
98  * releases the lock. This function must be called while holding the
99  * @queue's lock. If the reference count went to 0, the @queue will be
100  * destroyed and the memory allocated will be freed. So you are not
101  * allowed to use the @queue afterwards, as it might have disappeared.
102  * The obvious asymmetry (it is not named
103  * g_async_queue_unref_unlocked(<!-- -->)) is because the queue can't be
104  * unlocked after unreffing it, as it might already have disappeared.
105  **/
106 void 
107 g_async_queue_unref_and_unlock (GAsyncQueue *queue)
108 {
109   gboolean stop;
110
111   g_return_if_fail (queue);
112   g_return_if_fail (queue->ref_count > 0);
113
114   queue->ref_count--;
115   stop = (queue->ref_count == 0);
116   g_mutex_unlock (queue->mutex);
117   
118   if (stop)
119     {
120       g_return_if_fail (queue->waiting_threads == 0);
121       g_mutex_free (queue->mutex);
122       if (queue->cond)
123         g_cond_free (queue->cond);
124       g_queue_free (queue->queue);
125       g_free (queue);
126     }
127 }
128
129 /**
130  * g_async_queue_unref:
131  * @queue: a #GAsyncQueue.
132  * 
133  * Decreases the reference count of the asynchronous @queue by 1. If
134  * the reference count went to 0, the @queue will be destroyed and the
135  * memory allocated will be freed. So you are not allowed to use the
136  * @queue afterwards, as it might have disappeared.
137  **/
138 void 
139 g_async_queue_unref (GAsyncQueue *queue)
140 {
141   g_return_if_fail (queue);
142   g_return_if_fail (queue->ref_count > 0);
143
144   g_mutex_lock (queue->mutex);
145   g_async_queue_unref_and_unlock (queue);
146 }
147
148 /**
149  * g_async_queue_lock:
150  * @queue: a #GAsyncQueue.
151  * 
152  * Acquires the @queue's lock. After that you can only call the
153  * <function>g_async_queue_*_unlocked()</function> function variants on that
154  * @queue. Otherwise it will deadlock.
155  **/
156 void
157 g_async_queue_lock (GAsyncQueue *queue)
158 {
159   g_return_if_fail (queue);
160   g_return_if_fail (queue->ref_count > 0);
161
162   g_mutex_lock (queue->mutex);
163 }
164
165 /**
166  * g_async_queue_unlock:
167  * @queue: a #GAsyncQueue.
168  * 
169  * Releases the queue's lock.
170  **/
171 void 
172 g_async_queue_unlock (GAsyncQueue *queue)
173 {
174   g_return_if_fail (queue);
175   g_return_if_fail (queue->ref_count > 0);
176
177   g_mutex_unlock (queue->mutex);
178 }
179
180 /**
181  * g_async_queue_push:
182  * @queue: a #GAsyncQueue.
183  * @data: @data to push into the @queue.
184  *
185  * Pushes the @data into the @queue. @data must not be %NULL.
186  **/
187 void
188 g_async_queue_push (GAsyncQueue* queue, gpointer data)
189 {
190   g_return_if_fail (queue);
191   g_return_if_fail (queue->ref_count > 0);
192   g_return_if_fail (data);
193
194   g_mutex_lock (queue->mutex);
195   g_async_queue_push_unlocked (queue, data);
196   g_mutex_unlock (queue->mutex);
197 }
198
199 /**
200  * g_async_queue_push_unlocked:
201  * @queue: a #GAsyncQueue.
202  * @data: @data to push into the @queue.
203  * 
204  * Pushes the @data into the @queue. @data must not be %NULL. This
205  * function must be called while holding the @queue's lock.
206  **/
207 void
208 g_async_queue_push_unlocked (GAsyncQueue* queue, gpointer data)
209 {
210   g_return_if_fail (queue);
211   g_return_if_fail (queue->ref_count > 0);
212   g_return_if_fail (data);
213
214   g_queue_push_head (queue->queue, data);
215   if (queue->waiting_threads > 0)
216     g_cond_signal (queue->cond);
217 }
218
219 static gpointer
220 g_async_queue_pop_intern_unlocked (GAsyncQueue* queue, gboolean try, 
221                                    GTimeVal *end_time)
222 {
223   gpointer retval;
224
225   if (!g_queue_peek_tail (queue->queue))
226     {
227       if (try)
228         return NULL;
229       
230       if (!queue->cond)
231         queue->cond = g_cond_new ();
232
233       if (!end_time)
234         {
235           queue->waiting_threads++;
236           while (!g_queue_peek_tail (queue->queue))
237             g_cond_wait(queue->cond, queue->mutex);
238           queue->waiting_threads--;
239         }
240       else
241         {
242           queue->waiting_threads++;
243           while (!g_queue_peek_tail (queue->queue))
244             if (!g_cond_timed_wait (queue->cond, queue->mutex, end_time))
245               break;
246           queue->waiting_threads--;
247           if (!g_queue_peek_tail (queue->queue))
248             return NULL;
249         }
250     }
251
252   retval = g_queue_pop_tail (queue->queue);
253
254   g_assert (retval);
255
256   return retval;
257 }
258
259 /**
260  * g_async_queue_pop:
261  * @queue: a #GAsyncQueue.
262  * 
263  * Pops data from the @queue. This function blocks until data become
264  * available.
265  *
266  * Return value: data from the queue.
267  **/
268 gpointer
269 g_async_queue_pop (GAsyncQueue* queue)
270 {
271   gpointer retval;
272
273   g_return_val_if_fail (queue, NULL);
274   g_return_val_if_fail (queue->ref_count > 0, NULL);
275
276   g_mutex_lock (queue->mutex);
277   retval = g_async_queue_pop_intern_unlocked (queue, FALSE, NULL);
278   g_mutex_unlock (queue->mutex);
279
280   return retval;
281 }
282
283 /**
284  * g_async_queue_pop_unlocked:
285  * @queue: a #GAsyncQueue.
286  * 
287  * Pops data from the @queue. This function blocks until data become
288  * available. This function must be called while holding the @queue's
289  * lock.
290  *
291  * Return value: data from the queue.
292  **/
293 gpointer
294 g_async_queue_pop_unlocked (GAsyncQueue* queue)
295 {
296   g_return_val_if_fail (queue, NULL);
297   g_return_val_if_fail (queue->ref_count > 0, NULL);
298
299   return g_async_queue_pop_intern_unlocked (queue, FALSE, NULL);
300 }
301
302 /**
303  * g_async_queue_try_pop:
304  * @queue: a #GAsyncQueue.
305  * 
306  * Tries to pop data from the @queue. If no data is available, %NULL is
307  * returned.
308  *
309  * Return value: data from the queue or %NULL, when no data is
310  * available immediately.
311  **/
312 gpointer
313 g_async_queue_try_pop (GAsyncQueue* queue)
314 {
315   gpointer retval;
316
317   g_return_val_if_fail (queue, NULL);
318   g_return_val_if_fail (queue->ref_count > 0, NULL);
319
320   g_mutex_lock (queue->mutex);
321   retval = g_async_queue_pop_intern_unlocked (queue, TRUE, NULL);
322   g_mutex_unlock (queue->mutex);
323
324   return retval;
325 }
326
327 /**
328  * g_async_queue_try_pop_unlocked:
329  * @queue: a #GAsyncQueue.
330  * 
331  * Tries to pop data from the @queue. If no data is available, %NULL is
332  * returned. This function must be called while holding the @queue's
333  * lock.
334  *
335  * Return value: data from the queue or %NULL, when no data is
336  * available immediately.
337  **/
338 gpointer
339 g_async_queue_try_pop_unlocked (GAsyncQueue* queue)
340 {
341   g_return_val_if_fail (queue, NULL);
342   g_return_val_if_fail (queue->ref_count > 0, NULL);
343
344   return g_async_queue_pop_intern_unlocked (queue, TRUE, NULL);
345 }
346
347 /**
348  * g_async_queue_timed_pop:
349  * @queue: a #GAsyncQueue.
350  * @end_time: a #GTimeVal, determining the final time.
351  *
352  * Pops data from the @queue. If no data is received before @end_time,
353  * %NULL is returned.
354  *
355  * To easily calculate @end_time a combination of g_get_current_time()
356  * and g_time_val_add() can be used.
357  *
358  * Return value: data from the queue or %NULL, when no data is
359  * received before @end_time.
360  **/
361 gpointer
362 g_async_queue_timed_pop (GAsyncQueue* queue, GTimeVal *end_time)
363 {
364   gpointer retval;
365
366   g_return_val_if_fail (queue, NULL);
367   g_return_val_if_fail (queue->ref_count > 0, NULL);
368
369   g_mutex_lock (queue->mutex);
370   retval = g_async_queue_pop_intern_unlocked (queue, FALSE, end_time);
371   g_mutex_unlock (queue->mutex);
372
373   return retval;  
374 }
375
376 /**
377  * g_async_queue_timed_pop_unlocked:
378  * @queue: a #GAsyncQueue.
379  * @end_time: a #GTimeVal, determining the final time.
380  *
381  * Pops data from the @queue. If no data is received before @end_time,
382  * %NULL is returned. This function must be called while holding the
383  * @queue's lock.
384  *
385  * To easily calculate @end_time a combination of g_get_current_time()
386  * and g_time_val_add() can be used.
387  *
388  * Return value: data from the queue or %NULL, when no data is
389  * received before @end_time.
390  **/
391 gpointer
392 g_async_queue_timed_pop_unlocked (GAsyncQueue* queue, GTimeVal *end_time)
393 {
394   g_return_val_if_fail (queue, NULL);
395   g_return_val_if_fail (queue->ref_count > 0, NULL);
396
397   return g_async_queue_pop_intern_unlocked (queue, FALSE, end_time);
398 }
399
400 /**
401  * g_async_queue_length:
402  * @queue: a #GAsyncQueue.
403  * 
404  * Returns the length of the queue, negative values mean waiting
405  * threads, positive values mean available entries in the
406  * @queue. Actually this function returns the number of data items in
407  * the queue minus the number of waiting threads. Thus a return value
408  * of 0 could mean 'n' entries in the queue and 'n' thread waiting.
409  * That can happen due to locking of the queue or due to
410  * scheduling.  
411  *
412  * Return value: the length of the @queue.
413  **/
414 gint
415 g_async_queue_length (GAsyncQueue* queue)
416 {
417   gint retval;
418
419   g_return_val_if_fail (queue, 0);
420   g_return_val_if_fail (queue->ref_count > 0, 0);
421
422   g_mutex_lock (queue->mutex);
423   retval = queue->queue->length - queue->waiting_threads;
424   g_mutex_unlock (queue->mutex);
425
426   return retval;
427 }
428
429 /**
430  * g_async_queue_length_unlocked:
431  * @queue: a #GAsyncQueue.
432  * 
433  * Returns the length of the queue, negative values mean waiting
434  * threads, positive values mean available entries in the
435  * @queue. Actually this function returns the number of data items in
436  * the queue minus the number of waiting threads. Thus a return value
437  * of 0 could mean 'n' entries in the queue and 'n' thread waiting.
438  * That can happen due to locking of the queue or due to
439  * scheduling. This function must be called while holding the @queue's
440  * lock.
441  *
442  * Return value: the length of the @queue.
443  **/
444 gint
445 g_async_queue_length_unlocked (GAsyncQueue* queue)
446 {
447   g_return_val_if_fail (queue, 0);
448   g_return_val_if_fail (queue->ref_count > 0, 0);
449
450   return queue->queue->length - queue->waiting_threads;
451 }
452