Branch data Line data Source code
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 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General Public
20 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : /*
24 : : * MT safe
25 : : */
26 : :
27 : : #include "config.h"
28 : :
29 : : #include "gasyncqueue.h"
30 : : #include "gasyncqueueprivate.h"
31 : :
32 : : #include "gmain.h"
33 : : #include "gmem.h"
34 : : #include "gqueue.h"
35 : : #include "gtestutils.h"
36 : : #include "gtimer.h"
37 : : #include "gthread.h"
38 : : #include "deprecated/gthread.h"
39 : :
40 : : /**
41 : : * GAsyncQueue: (copy-func g_async_queue_ref) (free-func g_async_queue_unref)
42 : : *
43 : : * An opaque data structure which represents an asynchronous queue.
44 : : *
45 : : * It should only be accessed through the `g_async_queue_*` functions.
46 : : */
47 : : struct _GAsyncQueue
48 : : {
49 : : GMutex mutex;
50 : : GCond cond;
51 : : GQueue queue;
52 : : GDestroyNotify item_free_func;
53 : : guint waiting_threads;
54 : : gint ref_count;
55 : : };
56 : :
57 : : typedef struct
58 : : {
59 : : GCompareDataFunc func;
60 : : gpointer user_data;
61 : : } SortData;
62 : :
63 : : /**
64 : : * g_async_queue_new: (constructor)
65 : : *
66 : : * Creates a new asynchronous queue.
67 : : *
68 : : * Returns: (transfer full): a new #GAsyncQueue. Free with g_async_queue_unref()
69 : : */
70 : : GAsyncQueue *
71 : 362 : g_async_queue_new (void)
72 : : {
73 : 362 : return g_async_queue_new_full (NULL);
74 : : }
75 : :
76 : : /**
77 : : * g_async_queue_new_full: (constructor)
78 : : * @item_free_func: (nullable): function to free queue elements
79 : : *
80 : : * Creates a new asynchronous queue and sets up a destroy notify
81 : : * function that is used to free any remaining queue items when
82 : : * the queue is destroyed after the final unref.
83 : : *
84 : : * Returns: (transfer full): a new #GAsyncQueue. Free with g_async_queue_unref()
85 : : *
86 : : * Since: 2.16
87 : : */
88 : : GAsyncQueue *
89 : 601 : g_async_queue_new_full (GDestroyNotify item_free_func)
90 : : {
91 : : GAsyncQueue *queue;
92 : :
93 : 601 : queue = g_new (GAsyncQueue, 1);
94 : 601 : g_mutex_init (&queue->mutex);
95 : 601 : g_cond_init (&queue->cond);
96 : 601 : g_queue_init (&queue->queue);
97 : 601 : queue->waiting_threads = 0;
98 : 601 : queue->ref_count = 1;
99 : 601 : queue->item_free_func = item_free_func;
100 : :
101 : 601 : return queue;
102 : : }
103 : :
104 : : /**
105 : : * g_async_queue_ref:
106 : : * @queue: a #GAsyncQueue
107 : : *
108 : : * Increases the reference count of the asynchronous @queue by 1.
109 : : * You do not need to hold the lock to call this function.
110 : : *
111 : : * Returns: (transfer full): the @queue that was passed in (since 2.6)
112 : : */
113 : : GAsyncQueue *
114 : 2 : g_async_queue_ref (GAsyncQueue *queue)
115 : : {
116 : 2 : g_return_val_if_fail (queue, NULL);
117 : :
118 : 1 : g_atomic_int_inc (&queue->ref_count);
119 : :
120 : 1 : return queue;
121 : : }
122 : :
123 : : /**
124 : : * g_async_queue_ref_unlocked:
125 : : * @queue: a #GAsyncQueue
126 : : *
127 : : * Increases the reference count of the asynchronous @queue by 1.
128 : : *
129 : : * Deprecated: 2.8: Reference counting is done atomically.
130 : : * so g_async_queue_ref() can be used regardless of the @queue's
131 : : * lock.
132 : : */
133 : : void
134 : 2 : g_async_queue_ref_unlocked (GAsyncQueue *queue)
135 : : {
136 : 2 : g_return_if_fail (queue);
137 : :
138 : 1 : g_atomic_int_inc (&queue->ref_count);
139 : : }
140 : :
141 : : /**
142 : : * g_async_queue_unref_and_unlock:
143 : : * @queue: (transfer full): a #GAsyncQueue
144 : : *
145 : : * Decreases the reference count of the asynchronous @queue by 1
146 : : * and releases the lock. This function must be called while holding
147 : : * the @queue's lock. If the reference count went to 0, the @queue
148 : : * will be destroyed and the memory allocated will be freed.
149 : : *
150 : : * Deprecated: 2.8: Reference counting is done atomically.
151 : : * so g_async_queue_unref() can be used regardless of the @queue's
152 : : * lock.
153 : : */
154 : : void
155 : 2 : g_async_queue_unref_and_unlock (GAsyncQueue *queue)
156 : : {
157 : 2 : g_return_if_fail (queue);
158 : :
159 : 1 : g_mutex_unlock (&queue->mutex);
160 : 1 : g_async_queue_unref (queue);
161 : : }
162 : :
163 : : /**
164 : : * g_async_queue_unref:
165 : : * @queue: (transfer full): a #GAsyncQueue.
166 : : *
167 : : * Decreases the reference count of the asynchronous @queue by 1.
168 : : *
169 : : * If the reference count went to 0, the @queue will be destroyed
170 : : * and the memory allocated will be freed. So you are not allowed
171 : : * to use the @queue afterwards, as it might have disappeared.
172 : : * You do not need to hold the lock to call this function.
173 : : */
174 : : void
175 : 62 : g_async_queue_unref (GAsyncQueue *queue)
176 : : {
177 : 62 : g_return_if_fail (queue);
178 : :
179 : 61 : if (g_atomic_int_dec_and_test (&queue->ref_count))
180 : : {
181 : 59 : g_return_if_fail (queue->waiting_threads == 0);
182 : 59 : g_mutex_clear (&queue->mutex);
183 : 59 : g_cond_clear (&queue->cond);
184 : 59 : if (queue->item_free_func)
185 : 6 : g_queue_foreach (&queue->queue, (GFunc) queue->item_free_func, NULL);
186 : 59 : g_queue_clear (&queue->queue);
187 : 59 : g_free (queue);
188 : : }
189 : : }
190 : :
191 : : /**
192 : : * g_async_queue_lock:
193 : : * @queue: a #GAsyncQueue
194 : : *
195 : : * Acquires the @queue's lock. If another thread is already
196 : : * holding the lock, this call will block until the lock
197 : : * becomes available.
198 : : *
199 : : * Call g_async_queue_unlock() to drop the lock again.
200 : : *
201 : : * While holding the lock, you can only call the
202 : : * g_async_queue_*_unlocked() functions on @queue. Otherwise,
203 : : * deadlock may occur.
204 : : */
205 : : void
206 : 3895994 : g_async_queue_lock (GAsyncQueue *queue)
207 : : {
208 : 3895994 : g_return_if_fail (queue);
209 : :
210 : 3895993 : g_mutex_lock (&queue->mutex);
211 : : }
212 : :
213 : : /**
214 : : * g_async_queue_unlock:
215 : : * @queue: a #GAsyncQueue
216 : : *
217 : : * Releases the queue's lock.
218 : : *
219 : : * Calling this function when you have not acquired
220 : : * the with g_async_queue_lock() leads to undefined
221 : : * behaviour.
222 : : */
223 : : void
224 : 3895698 : g_async_queue_unlock (GAsyncQueue *queue)
225 : : {
226 : 3895698 : g_return_if_fail (queue);
227 : :
228 : 3895697 : g_mutex_unlock (&queue->mutex);
229 : : }
230 : :
231 : : /**
232 : : * g_async_queue_push:
233 : : * @queue: a #GAsyncQueue
234 : : * @data: (not nullable) (transfer full): data to push onto the @queue
235 : : *
236 : : * Pushes the @data into the @queue.
237 : : *
238 : : * The @data parameter must not be %NULL.
239 : : */
240 : : void
241 : 100037 : g_async_queue_push (GAsyncQueue *queue,
242 : : gpointer data)
243 : : {
244 : 100037 : g_return_if_fail (queue);
245 : 100036 : g_return_if_fail (data);
246 : :
247 : 100035 : g_mutex_lock (&queue->mutex);
248 : 100035 : g_async_queue_push_unlocked (queue, data);
249 : 100035 : g_mutex_unlock (&queue->mutex);
250 : : }
251 : :
252 : : /**
253 : : * g_async_queue_push_unlocked:
254 : : * @queue: a #GAsyncQueue
255 : : * @data: (not nullable) (transfer full): data to push onto the @queue
256 : : *
257 : : * Pushes the @data into the @queue.
258 : : *
259 : : * The @data parameter must not be %NULL.
260 : : *
261 : : * This function must be called while holding the @queue's lock.
262 : : */
263 : : void
264 : 203046 : g_async_queue_push_unlocked (GAsyncQueue *queue,
265 : : gpointer data)
266 : : {
267 : 203046 : g_return_if_fail (queue);
268 : 203045 : g_return_if_fail (data);
269 : :
270 : 203044 : g_queue_push_head (&queue->queue, data);
271 : 203044 : if (queue->waiting_threads > 0)
272 : 51523 : g_cond_signal (&queue->cond);
273 : : }
274 : :
275 : : /**
276 : : * g_async_queue_push_sorted:
277 : : * @queue: a #GAsyncQueue
278 : : * @data: (not nullable) (transfer full): the @data to push into the @queue
279 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
280 : : * @user_data: user data passed to @func.
281 : : *
282 : : * Inserts @data into @queue using @func to determine the new
283 : : * position.
284 : : *
285 : : * This function requires that the @queue is sorted before pushing on
286 : : * new elements, see g_async_queue_sort().
287 : : *
288 : : * This function will lock @queue before it sorts the queue and unlock
289 : : * it when it is finished.
290 : : *
291 : : * For an example of @func see g_async_queue_sort().
292 : : *
293 : : * Since: 2.10
294 : : */
295 : : void
296 : 3 : g_async_queue_push_sorted (GAsyncQueue *queue,
297 : : gpointer data,
298 : : GCompareDataFunc func,
299 : : gpointer user_data)
300 : : {
301 : 3 : g_return_if_fail (queue != NULL);
302 : :
303 : 2 : g_mutex_lock (&queue->mutex);
304 : 2 : g_async_queue_push_sorted_unlocked (queue, data, func, user_data);
305 : 2 : g_mutex_unlock (&queue->mutex);
306 : : }
307 : :
308 : : static gint
309 : 3988 : g_async_queue_invert_compare (gpointer v1,
310 : : gpointer v2,
311 : : SortData *sd)
312 : : {
313 : 3988 : return -sd->func (v1, v2, sd->user_data);
314 : : }
315 : :
316 : : /**
317 : : * g_async_queue_push_sorted_unlocked:
318 : : * @queue: a #GAsyncQueue
319 : : * @data: (not nullable) (transfer full): the data to push into the @queue
320 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
321 : : * @user_data: user data passed to @func.
322 : : *
323 : : * Inserts @data into @queue using @func to determine the new
324 : : * position.
325 : : *
326 : : * The sort function @func is passed two elements of the @queue.
327 : : * It should return 0 if they are equal, a negative value if the
328 : : * first element should be higher in the @queue or a positive value
329 : : * if the first element should be lower in the @queue than the second
330 : : * element.
331 : : *
332 : : * This function requires that the @queue is sorted before pushing on
333 : : * new elements, see g_async_queue_sort().
334 : : *
335 : : * This function must be called while holding the @queue's lock.
336 : : *
337 : : * For an example of @func see g_async_queue_sort().
338 : : *
339 : : * Since: 2.10
340 : : */
341 : : void
342 : 3597 : g_async_queue_push_sorted_unlocked (GAsyncQueue *queue,
343 : : gpointer data,
344 : : GCompareDataFunc func,
345 : : gpointer user_data)
346 : : {
347 : : SortData sd;
348 : :
349 : 3597 : g_return_if_fail (queue != NULL);
350 : 3596 : g_return_if_fail (data != NULL);
351 : :
352 : 3596 : sd.func = func;
353 : 3596 : sd.user_data = user_data;
354 : :
355 : 3596 : g_queue_insert_sorted (&queue->queue,
356 : : data,
357 : : (GCompareDataFunc)g_async_queue_invert_compare,
358 : : &sd);
359 : 3596 : if (queue->waiting_threads > 0)
360 : 2623 : g_cond_signal (&queue->cond);
361 : : }
362 : :
363 : : static gpointer
364 : 206645 : g_async_queue_pop_intern_unlocked (GAsyncQueue *queue,
365 : : gboolean wait,
366 : : gint64 end_time)
367 : : {
368 : : gpointer retval;
369 : :
370 : 206645 : if (!g_queue_peek_tail_link (&queue->queue) && wait)
371 : : {
372 : 31458 : queue->waiting_threads++;
373 : 91872 : while (!g_queue_peek_tail_link (&queue->queue))
374 : : {
375 : 60912 : if (end_time == -1)
376 : 57916 : g_cond_wait (&queue->cond, &queue->mutex);
377 : : else
378 : : {
379 : 2996 : if (!g_cond_wait_until (&queue->cond, &queue->mutex, end_time))
380 : 177 : break;
381 : : }
382 : : }
383 : 31137 : queue->waiting_threads--;
384 : : }
385 : :
386 : 206324 : retval = g_queue_pop_tail (&queue->queue);
387 : :
388 : 206324 : g_assert (retval || !wait || end_time > 0);
389 : :
390 : 206324 : return retval;
391 : : }
392 : :
393 : : /**
394 : : * g_async_queue_pop:
395 : : * @queue: a #GAsyncQueue
396 : : *
397 : : * Pops data from the @queue. If @queue is empty, this function
398 : : * blocks until data becomes available.
399 : : *
400 : : * Returns: (not nullable) (transfer full): data from the queue
401 : : */
402 : : gpointer
403 : 101183 : g_async_queue_pop (GAsyncQueue *queue)
404 : : {
405 : : gpointer retval;
406 : :
407 : 101183 : g_return_val_if_fail (queue, NULL);
408 : :
409 : 101182 : g_mutex_lock (&queue->mutex);
410 : 101182 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
411 : 101182 : g_mutex_unlock (&queue->mutex);
412 : :
413 : 101182 : return retval;
414 : : }
415 : :
416 : : /**
417 : : * g_async_queue_pop_unlocked:
418 : : * @queue: a #GAsyncQueue
419 : : *
420 : : * Pops data from the @queue. If @queue is empty, this function
421 : : * blocks until data becomes available.
422 : : *
423 : : * This function must be called while holding the @queue's lock.
424 : : *
425 : : * Returns: (not nullable) (transfer full): data from the queue.
426 : : */
427 : : gpointer
428 : 101083 : g_async_queue_pop_unlocked (GAsyncQueue *queue)
429 : : {
430 : 101083 : g_return_val_if_fail (queue, NULL);
431 : :
432 : 101082 : return g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
433 : : }
434 : :
435 : : /**
436 : : * g_async_queue_try_pop:
437 : : * @queue: a #GAsyncQueue
438 : : *
439 : : * Tries to pop data from the @queue. If no data is available,
440 : : * %NULL is returned.
441 : : *
442 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
443 : : * data is available immediately.
444 : : */
445 : : gpointer
446 : 7 : g_async_queue_try_pop (GAsyncQueue *queue)
447 : : {
448 : : gpointer retval;
449 : :
450 : 7 : g_return_val_if_fail (queue, NULL);
451 : :
452 : 6 : g_mutex_lock (&queue->mutex);
453 : 6 : retval = g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
454 : 6 : g_mutex_unlock (&queue->mutex);
455 : :
456 : 6 : return retval;
457 : : }
458 : :
459 : : /**
460 : : * g_async_queue_try_pop_unlocked:
461 : : * @queue: a #GAsyncQueue
462 : : *
463 : : * Tries to pop data from the @queue. If no data is available,
464 : : * %NULL is returned.
465 : : *
466 : : * This function must be called while holding the @queue's lock.
467 : : *
468 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
469 : : * data is available immediately.
470 : : */
471 : : gpointer
472 : 2 : g_async_queue_try_pop_unlocked (GAsyncQueue *queue)
473 : : {
474 : 2 : g_return_val_if_fail (queue, NULL);
475 : :
476 : 1 : return g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
477 : : }
478 : :
479 : : /**
480 : : * g_async_queue_timeout_pop:
481 : : * @queue: a #GAsyncQueue
482 : : * @timeout: the number of microseconds to wait
483 : : *
484 : : * Pops data from the @queue. If the queue is empty, blocks for
485 : : * @timeout microseconds, or until data becomes available.
486 : : *
487 : : * If no data is received before the timeout, %NULL is returned.
488 : : *
489 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
490 : : * data is received before the timeout.
491 : : */
492 : : gpointer
493 : 86 : g_async_queue_timeout_pop (GAsyncQueue *queue,
494 : : guint64 timeout)
495 : : {
496 : 86 : gint64 end_time = g_get_monotonic_time () + timeout;
497 : : gpointer retval;
498 : :
499 : 86 : g_return_val_if_fail (queue != NULL, NULL);
500 : :
501 : 85 : g_mutex_lock (&queue->mutex);
502 : 85 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
503 : 59 : g_mutex_unlock (&queue->mutex);
504 : :
505 : 59 : return retval;
506 : : }
507 : :
508 : : /**
509 : : * g_async_queue_timeout_pop_unlocked:
510 : : * @queue: a #GAsyncQueue
511 : : * @timeout: the number of microseconds to wait
512 : : *
513 : : * Pops data from the @queue. If the queue is empty, blocks for
514 : : * @timeout microseconds, or until data becomes available.
515 : : *
516 : : * If no data is received before the timeout, %NULL is returned.
517 : : *
518 : : * This function must be called while holding the @queue's lock.
519 : : *
520 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
521 : : * data is received before the timeout.
522 : : */
523 : : gpointer
524 : 4286 : g_async_queue_timeout_pop_unlocked (GAsyncQueue *queue,
525 : : guint64 timeout)
526 : : {
527 : 4286 : gint64 end_time = g_get_monotonic_time () + timeout;
528 : :
529 : 4286 : g_return_val_if_fail (queue != NULL, NULL);
530 : :
531 : 4285 : return g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
532 : : }
533 : :
534 : : /**
535 : : * g_async_queue_timed_pop:
536 : : * @queue: a #GAsyncQueue
537 : : * @end_time: a #GTimeVal, determining the final time
538 : : *
539 : : * Pops data from the @queue. If the queue is empty, blocks until
540 : : * @end_time or until data becomes available.
541 : : *
542 : : * If no data is received before @end_time, %NULL is returned.
543 : : *
544 : : * To easily calculate @end_time, a combination of g_get_real_time()
545 : : * and g_time_val_add() can be used.
546 : : *
547 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
548 : : * data is received before @end_time.
549 : : *
550 : : * Deprecated: use g_async_queue_timeout_pop().
551 : : */
552 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
553 : : gpointer
554 : 3 : g_async_queue_timed_pop (GAsyncQueue *queue,
555 : : GTimeVal *end_time)
556 : : {
557 : : gint64 m_end_time;
558 : : gpointer retval;
559 : :
560 : 3 : g_return_val_if_fail (queue, NULL);
561 : :
562 : 2 : if (end_time != NULL)
563 : : {
564 : 1 : m_end_time = g_get_monotonic_time () +
565 : 1 : ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
566 : : }
567 : : else
568 : 1 : m_end_time = -1;
569 : :
570 : 2 : g_mutex_lock (&queue->mutex);
571 : 2 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
572 : 2 : g_mutex_unlock (&queue->mutex);
573 : :
574 : 2 : return retval;
575 : : }
576 : : G_GNUC_END_IGNORE_DEPRECATIONS
577 : :
578 : : /**
579 : : * g_async_queue_timed_pop_unlocked:
580 : : * @queue: a #GAsyncQueue
581 : : * @end_time: a #GTimeVal, determining the final time
582 : : *
583 : : * Pops data from the @queue. If the queue is empty, blocks until
584 : : * @end_time or until data becomes available.
585 : : *
586 : : * If no data is received before @end_time, %NULL is returned.
587 : : *
588 : : * To easily calculate @end_time, a combination of g_get_real_time()
589 : : * and g_time_val_add() can be used.
590 : : *
591 : : * This function must be called while holding the @queue's lock.
592 : : *
593 : : * Returns: (nullable) (transfer full): data from the queue or %NULL, when no
594 : : * data is received before @end_time.
595 : : *
596 : : * Deprecated: use g_async_queue_timeout_pop_unlocked().
597 : : */
598 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
599 : : gpointer
600 : 3 : g_async_queue_timed_pop_unlocked (GAsyncQueue *queue,
601 : : GTimeVal *end_time)
602 : : {
603 : : gint64 m_end_time;
604 : :
605 : 3 : g_return_val_if_fail (queue, NULL);
606 : :
607 : 2 : if (end_time != NULL)
608 : : {
609 : 1 : m_end_time = g_get_monotonic_time () +
610 : 1 : ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
611 : : }
612 : : else
613 : 1 : m_end_time = -1;
614 : :
615 : 2 : return g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
616 : : }
617 : : G_GNUC_END_IGNORE_DEPRECATIONS
618 : :
619 : : /**
620 : : * g_async_queue_length:
621 : : * @queue: a #GAsyncQueue.
622 : : *
623 : : * Returns the length of the queue.
624 : : *
625 : : * Actually this function returns the number of data items in
626 : : * the queue minus the number of waiting threads, so a negative
627 : : * value means waiting threads, and a positive value means available
628 : : * entries in the @queue. A return value of 0 could mean n entries
629 : : * in the queue and n threads waiting. This can happen due to locking
630 : : * of the queue or due to scheduling.
631 : : *
632 : : * Returns: the length of the @queue
633 : : */
634 : : gint
635 : 3780 : g_async_queue_length (GAsyncQueue *queue)
636 : : {
637 : : gint retval;
638 : :
639 : 3780 : g_return_val_if_fail (queue, 0);
640 : :
641 : 3779 : g_mutex_lock (&queue->mutex);
642 : 3779 : retval = queue->queue.length - queue->waiting_threads;
643 : 3779 : g_mutex_unlock (&queue->mutex);
644 : :
645 : 3779 : return retval;
646 : : }
647 : :
648 : : /**
649 : : * g_async_queue_length_unlocked:
650 : : * @queue: a #GAsyncQueue
651 : : *
652 : : * Returns the length of the queue.
653 : : *
654 : : * Actually this function returns the number of data items in
655 : : * the queue minus the number of waiting threads, so a negative
656 : : * value means waiting threads, and a positive value means available
657 : : * entries in the @queue. A return value of 0 could mean n entries
658 : : * in the queue and n threads waiting. This can happen due to locking
659 : : * of the queue or due to scheduling.
660 : : *
661 : : * This function must be called while holding the @queue's lock.
662 : : *
663 : : * Returns: the length of the @queue.
664 : : */
665 : : gint
666 : 131345 : g_async_queue_length_unlocked (GAsyncQueue *queue)
667 : : {
668 : 131345 : g_return_val_if_fail (queue, 0);
669 : :
670 : 131344 : return queue->queue.length - queue->waiting_threads;
671 : : }
672 : :
673 : : /**
674 : : * g_async_queue_sort:
675 : : * @queue: a #GAsyncQueue
676 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
677 : : * @user_data: user data passed to @func
678 : : *
679 : : * Sorts @queue using @func.
680 : : *
681 : : * The sort function @func is passed two elements of the @queue.
682 : : * It should return 0 if they are equal, a negative value if the
683 : : * first element should be higher in the @queue or a positive value
684 : : * if the first element should be lower in the @queue than the second
685 : : * element.
686 : : *
687 : : * This function will lock @queue before it sorts the queue and unlock
688 : : * it when it is finished.
689 : : *
690 : : * If you were sorting a list of priority numbers to make sure the
691 : : * lowest priority would be at the top of the queue, you could use:
692 : : * |[<!-- language="C" -->
693 : : * gint32 id1;
694 : : * gint32 id2;
695 : : *
696 : : * id1 = GPOINTER_TO_INT (element1);
697 : : * id2 = GPOINTER_TO_INT (element2);
698 : : *
699 : : * return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
700 : : * ]|
701 : : *
702 : : * Since: 2.10
703 : : */
704 : : void
705 : 3 : g_async_queue_sort (GAsyncQueue *queue,
706 : : GCompareDataFunc func,
707 : : gpointer user_data)
708 : : {
709 : 3 : g_return_if_fail (queue != NULL);
710 : 2 : g_return_if_fail (func != NULL);
711 : :
712 : 1 : g_mutex_lock (&queue->mutex);
713 : 1 : g_async_queue_sort_unlocked (queue, func, user_data);
714 : 1 : g_mutex_unlock (&queue->mutex);
715 : : }
716 : :
717 : : /**
718 : : * g_async_queue_sort_unlocked:
719 : : * @queue: a #GAsyncQueue
720 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
721 : : * @user_data: user data passed to @func
722 : : *
723 : : * Sorts @queue using @func.
724 : : *
725 : : * The sort function @func is passed two elements of the @queue.
726 : : * It should return 0 if they are equal, a negative value if the
727 : : * first element should be higher in the @queue or a positive value
728 : : * if the first element should be lower in the @queue than the second
729 : : * element.
730 : : *
731 : : * This function must be called while holding the @queue's lock.
732 : : *
733 : : * Since: 2.10
734 : : */
735 : : void
736 : 177 : g_async_queue_sort_unlocked (GAsyncQueue *queue,
737 : : GCompareDataFunc func,
738 : : gpointer user_data)
739 : : {
740 : : SortData sd;
741 : :
742 : 178 : g_return_if_fail (queue != NULL);
743 : 176 : g_return_if_fail (func != NULL);
744 : :
745 : 175 : sd.func = func;
746 : 175 : sd.user_data = user_data;
747 : :
748 : 175 : g_queue_sort (&queue->queue,
749 : : (GCompareDataFunc)g_async_queue_invert_compare,
750 : : &sd);
751 : : }
752 : :
753 : : /**
754 : : * g_async_queue_remove:
755 : : * @queue: a #GAsyncQueue
756 : : * @item: (not nullable) (transfer none): the data to remove from the @queue
757 : : *
758 : : * Remove an item from the queue.
759 : : *
760 : : * Returns: %TRUE if the item was removed
761 : : *
762 : : * Since: 2.46
763 : : */
764 : : gboolean
765 : 49 : g_async_queue_remove (GAsyncQueue *queue,
766 : : gpointer item)
767 : : {
768 : : gboolean ret;
769 : :
770 : 49 : g_return_val_if_fail (queue != NULL, FALSE);
771 : 48 : g_return_val_if_fail (item != NULL, FALSE);
772 : :
773 : 47 : g_mutex_lock (&queue->mutex);
774 : 47 : ret = g_async_queue_remove_unlocked (queue, item);
775 : 47 : g_mutex_unlock (&queue->mutex);
776 : :
777 : 47 : return ret;
778 : : }
779 : :
780 : : /**
781 : : * g_async_queue_remove_unlocked:
782 : : * @queue: a #GAsyncQueue
783 : : * @item: (not nullable) (transfer none): the data to remove from the @queue
784 : : *
785 : : * Remove an item from the queue.
786 : : *
787 : : * This function must be called while holding the @queue's lock.
788 : : *
789 : : * Returns: %TRUE if the item was removed
790 : : *
791 : : * Since: 2.46
792 : : */
793 : : gboolean
794 : 1087 : g_async_queue_remove_unlocked (GAsyncQueue *queue,
795 : : gpointer item)
796 : : {
797 : 1087 : g_return_val_if_fail (queue != NULL, FALSE);
798 : 1086 : g_return_val_if_fail (item != NULL, FALSE);
799 : :
800 : 1085 : return g_queue_remove (&queue->queue, item);
801 : : }
802 : :
803 : : /**
804 : : * g_async_queue_push_front:
805 : : * @queue: a #GAsyncQueue
806 : : * @item: (not nullable) (transfer full): data to push into the @queue
807 : : *
808 : : * Pushes the @item into the @queue. @item must not be %NULL.
809 : : * In contrast to g_async_queue_push(), this function
810 : : * pushes the new item ahead of the items already in the queue,
811 : : * so that it will be the next one to be popped off the queue.
812 : : *
813 : : * Since: 2.46
814 : : */
815 : : void
816 : 3 : g_async_queue_push_front (GAsyncQueue *queue,
817 : : gpointer item)
818 : : {
819 : 3 : g_return_if_fail (queue != NULL);
820 : 2 : g_return_if_fail (item != NULL);
821 : :
822 : 1 : g_mutex_lock (&queue->mutex);
823 : 1 : g_async_queue_push_front_unlocked (queue, item);
824 : 1 : g_mutex_unlock (&queue->mutex);
825 : : }
826 : :
827 : : /**
828 : : * g_async_queue_push_front_unlocked:
829 : : * @queue: a #GAsyncQueue
830 : : * @item: (not nullable) (transfer full): data to push into the @queue
831 : : *
832 : : * Pushes the @item into the @queue. @item must not be %NULL.
833 : : * In contrast to g_async_queue_push_unlocked(), this function
834 : : * pushes the new item ahead of the items already in the queue,
835 : : * so that it will be the next one to be popped off the queue.
836 : : *
837 : : * This function must be called while holding the @queue's lock.
838 : : *
839 : : * Since: 2.46
840 : : */
841 : : void
842 : 1017 : g_async_queue_push_front_unlocked (GAsyncQueue *queue,
843 : : gpointer item)
844 : : {
845 : 1017 : g_return_if_fail (queue != NULL);
846 : 1016 : g_return_if_fail (item != NULL);
847 : :
848 : 1015 : g_queue_push_tail (&queue->queue, item);
849 : 1015 : if (queue->waiting_threads > 0)
850 : 4 : g_cond_signal (&queue->cond);
851 : : }
852 : :
853 : : /*
854 : : * Private API
855 : : */
856 : :
857 : : GMutex *
858 : 301 : _g_async_queue_get_mutex (GAsyncQueue *queue)
859 : : {
860 : 301 : g_return_val_if_fail (queue, NULL);
861 : :
862 : 301 : return &queue->mutex;
863 : : }
|