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:
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 : 339 : g_async_queue_new (void)
72 : : {
73 : 339 : 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 : 563 : g_async_queue_new_full (GDestroyNotify item_free_func)
90 : : {
91 : : GAsyncQueue *queue;
92 : :
93 : 563 : queue = g_new (GAsyncQueue, 1);
94 : 563 : g_mutex_init (&queue->mutex);
95 : 563 : g_cond_init (&queue->cond);
96 : 563 : g_queue_init (&queue->queue);
97 : 563 : queue->waiting_threads = 0;
98 : 563 : queue->ref_count = 1;
99 : 563 : queue->item_free_func = item_free_func;
100 : :
101 : 563 : 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: 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 : 60 : g_async_queue_unref (GAsyncQueue *queue)
176 : : {
177 : 60 : g_return_if_fail (queue);
178 : :
179 [ + + ]: 59 : if (g_atomic_int_dec_and_test (&queue->ref_count))
180 : : {
181 : 57 : g_return_if_fail (queue->waiting_threads == 0);
182 : 57 : g_mutex_clear (&queue->mutex);
183 : 57 : g_cond_clear (&queue->cond);
184 [ + + ]: 57 : if (queue->item_free_func)
185 : 6 : g_queue_foreach (&queue->queue, (GFunc) queue->item_free_func, NULL);
186 : 57 : g_queue_clear (&queue->queue);
187 : 57 : 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 : 4358757 : g_async_queue_lock (GAsyncQueue *queue)
207 : : {
208 : 4358757 : g_return_if_fail (queue);
209 : :
210 : 4358756 : 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 : 4358474 : g_async_queue_unlock (GAsyncQueue *queue)
225 : : {
226 : 4358474 : g_return_if_fail (queue);
227 : :
228 : 4358473 : g_mutex_unlock (&queue->mutex);
229 : : }
230 : :
231 : : /**
232 : : * g_async_queue_push:
233 : : * @queue: a #GAsyncQueue
234 : : * @data: (not nullable): 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): 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 : 203140 : g_async_queue_push_unlocked (GAsyncQueue *queue,
265 : : gpointer data)
266 : : {
267 : 203140 : g_return_if_fail (queue);
268 : 203139 : g_return_if_fail (data);
269 : :
270 : 203138 : g_queue_push_head (&queue->queue, data);
271 [ + + ]: 203138 : if (queue->waiting_threads > 0)
272 : 126329 : g_cond_signal (&queue->cond);
273 : : }
274 : :
275 : : /**
276 : : * g_async_queue_push_sorted:
277 : : * @queue: a #GAsyncQueue
278 : : * @data: (not nullable): 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 : 4144 : g_async_queue_invert_compare (gpointer v1,
310 : : gpointer v2,
311 : : SortData *sd)
312 : : {
313 : 4144 : return -sd->func (v1, v2, sd->user_data);
314 : : }
315 : :
316 : : /**
317 : : * g_async_queue_push_sorted_unlocked:
318 : : * @queue: a #GAsyncQueue
319 : : * @data: 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 : 3566 : g_async_queue_push_sorted_unlocked (GAsyncQueue *queue,
343 : : gpointer data,
344 : : GCompareDataFunc func,
345 : : gpointer user_data)
346 : : {
347 : : SortData sd;
348 : :
349 : 3566 : g_return_if_fail (queue != NULL);
350 : :
351 : 3565 : sd.func = func;
352 : 3565 : sd.user_data = user_data;
353 : :
354 : 3565 : g_queue_insert_sorted (&queue->queue,
355 : : data,
356 : : (GCompareDataFunc)g_async_queue_invert_compare,
357 : : &sd);
358 [ + + ]: 3565 : if (queue->waiting_threads > 0)
359 : 2354 : g_cond_signal (&queue->cond);
360 : : }
361 : :
362 : : static gpointer
363 : 206738 : g_async_queue_pop_intern_unlocked (GAsyncQueue *queue,
364 : : gboolean wait,
365 : : gint64 end_time)
366 : : {
367 : : gpointer retval;
368 : :
369 [ + + + + ]: 206738 : if (!g_queue_peek_tail_link (&queue->queue) && wait)
370 : : {
371 : 14388 : queue->waiting_threads++;
372 [ + + ]: 34090 : while (!g_queue_peek_tail_link (&queue->queue))
373 : : {
374 [ + + ]: 20237 : if (end_time == -1)
375 : 17308 : g_cond_wait (&queue->cond, &queue->mutex);
376 : : else
377 : : {
378 [ + + ]: 2929 : if (!g_cond_wait_until (&queue->cond, &queue->mutex, end_time))
379 : 242 : break;
380 : : }
381 : : }
382 : 14095 : queue->waiting_threads--;
383 : : }
384 : :
385 : 206445 : retval = g_queue_pop_tail (&queue->queue);
386 : :
387 : 206445 : g_assert (retval || !wait || end_time > 0);
388 : :
389 : 206445 : return retval;
390 : : }
391 : :
392 : : /**
393 : : * g_async_queue_pop:
394 : : * @queue: a #GAsyncQueue
395 : : *
396 : : * Pops data from the @queue. If @queue is empty, this function
397 : : * blocks until data becomes available.
398 : : *
399 : : * Returns: data from the queue
400 : : */
401 : : gpointer
402 : 101251 : g_async_queue_pop (GAsyncQueue *queue)
403 : : {
404 : : gpointer retval;
405 : :
406 : 101251 : g_return_val_if_fail (queue, NULL);
407 : :
408 : 101250 : g_mutex_lock (&queue->mutex);
409 : 101250 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
410 : 101250 : g_mutex_unlock (&queue->mutex);
411 : :
412 : 101250 : return retval;
413 : : }
414 : :
415 : : /**
416 : : * g_async_queue_pop_unlocked:
417 : : * @queue: a #GAsyncQueue
418 : : *
419 : : * Pops data from the @queue. If @queue is empty, this function
420 : : * blocks until data becomes available.
421 : : *
422 : : * This function must be called while holding the @queue's lock.
423 : : *
424 : : * Returns: data from the queue.
425 : : */
426 : : gpointer
427 : 101117 : g_async_queue_pop_unlocked (GAsyncQueue *queue)
428 : : {
429 : 101117 : g_return_val_if_fail (queue, NULL);
430 : :
431 : 101116 : return g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
432 : : }
433 : :
434 : : /**
435 : : * g_async_queue_try_pop:
436 : : * @queue: a #GAsyncQueue
437 : : *
438 : : * Tries to pop data from the @queue. If no data is available,
439 : : * %NULL is returned.
440 : : *
441 : : * Returns: (nullable): data from the queue or %NULL, when no data is
442 : : * available immediately.
443 : : */
444 : : gpointer
445 : 7 : g_async_queue_try_pop (GAsyncQueue *queue)
446 : : {
447 : : gpointer retval;
448 : :
449 : 7 : g_return_val_if_fail (queue, NULL);
450 : :
451 : 6 : g_mutex_lock (&queue->mutex);
452 : 6 : retval = g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
453 : 6 : g_mutex_unlock (&queue->mutex);
454 : :
455 : 6 : return retval;
456 : : }
457 : :
458 : : /**
459 : : * g_async_queue_try_pop_unlocked:
460 : : * @queue: a #GAsyncQueue
461 : : *
462 : : * Tries to pop data from the @queue. If no data is available,
463 : : * %NULL is returned.
464 : : *
465 : : * This function must be called while holding the @queue's lock.
466 : : *
467 : : * Returns: (nullable): data from the queue or %NULL, when no data is
468 : : * available immediately.
469 : : */
470 : : gpointer
471 : 2 : g_async_queue_try_pop_unlocked (GAsyncQueue *queue)
472 : : {
473 : 2 : g_return_val_if_fail (queue, NULL);
474 : :
475 : 1 : return g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
476 : : }
477 : :
478 : : /**
479 : : * g_async_queue_timeout_pop:
480 : : * @queue: a #GAsyncQueue
481 : : * @timeout: the number of microseconds to wait
482 : : *
483 : : * Pops data from the @queue. If the queue is empty, blocks for
484 : : * @timeout microseconds, or until data becomes available.
485 : : *
486 : : * If no data is received before the timeout, %NULL is returned.
487 : : *
488 : : * Returns: (nullable): data from the queue or %NULL, when no data is
489 : : * received before the timeout.
490 : : */
491 : : gpointer
492 : 50 : g_async_queue_timeout_pop (GAsyncQueue *queue,
493 : : guint64 timeout)
494 : : {
495 : 50 : gint64 end_time = g_get_monotonic_time () + timeout;
496 : : gpointer retval;
497 : :
498 : 50 : g_return_val_if_fail (queue != NULL, NULL);
499 : :
500 : 49 : g_mutex_lock (&queue->mutex);
501 : 49 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
502 : 38 : g_mutex_unlock (&queue->mutex);
503 : :
504 : 38 : return retval;
505 : : }
506 : :
507 : : /**
508 : : * g_async_queue_timeout_pop_unlocked:
509 : : * @queue: a #GAsyncQueue
510 : : * @timeout: the number of microseconds to wait
511 : : *
512 : : * Pops data from the @queue. If the queue is empty, blocks for
513 : : * @timeout microseconds, or until data becomes available.
514 : : *
515 : : * If no data is received before the timeout, %NULL is returned.
516 : : *
517 : : * This function must be called while holding the @queue's lock.
518 : : *
519 : : * Returns: (nullable): data from the queue or %NULL, when no data is
520 : : * received before the timeout.
521 : : */
522 : : gpointer
523 : 4313 : g_async_queue_timeout_pop_unlocked (GAsyncQueue *queue,
524 : : guint64 timeout)
525 : : {
526 : 4313 : gint64 end_time = g_get_monotonic_time () + timeout;
527 : :
528 : 4313 : g_return_val_if_fail (queue != NULL, NULL);
529 : :
530 : 4312 : return g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
531 : : }
532 : :
533 : : /**
534 : : * g_async_queue_timed_pop:
535 : : * @queue: a #GAsyncQueue
536 : : * @end_time: a #GTimeVal, determining the final time
537 : : *
538 : : * Pops data from the @queue. If the queue is empty, blocks until
539 : : * @end_time or until data becomes available.
540 : : *
541 : : * If no data is received before @end_time, %NULL is returned.
542 : : *
543 : : * To easily calculate @end_time, a combination of g_get_real_time()
544 : : * and g_time_val_add() can be used.
545 : : *
546 : : * Returns: (nullable): data from the queue or %NULL, when no data is
547 : : * received before @end_time.
548 : : *
549 : : * Deprecated: use g_async_queue_timeout_pop().
550 : : */
551 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
552 : : gpointer
553 : 3 : g_async_queue_timed_pop (GAsyncQueue *queue,
554 : : GTimeVal *end_time)
555 : : {
556 : : gint64 m_end_time;
557 : : gpointer retval;
558 : :
559 : 3 : g_return_val_if_fail (queue, NULL);
560 : :
561 [ + + ]: 2 : if (end_time != NULL)
562 : : {
563 : 1 : m_end_time = g_get_monotonic_time () +
564 : 1 : ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
565 : : }
566 : : else
567 : 1 : m_end_time = -1;
568 : :
569 : 2 : g_mutex_lock (&queue->mutex);
570 : 2 : retval = g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
571 : 2 : g_mutex_unlock (&queue->mutex);
572 : :
573 : 2 : return retval;
574 : : }
575 : : G_GNUC_END_IGNORE_DEPRECATIONS
576 : :
577 : : /**
578 : : * g_async_queue_timed_pop_unlocked:
579 : : * @queue: a #GAsyncQueue
580 : : * @end_time: a #GTimeVal, determining the final time
581 : : *
582 : : * Pops data from the @queue. If the queue is empty, blocks until
583 : : * @end_time or until data becomes available.
584 : : *
585 : : * If no data is received before @end_time, %NULL is returned.
586 : : *
587 : : * To easily calculate @end_time, a combination of g_get_real_time()
588 : : * and g_time_val_add() can be used.
589 : : *
590 : : * This function must be called while holding the @queue's lock.
591 : : *
592 : : * Returns: (nullable): data from the queue or %NULL, when no data is
593 : : * received before @end_time.
594 : : *
595 : : * Deprecated: use g_async_queue_timeout_pop_unlocked().
596 : : */
597 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
598 : : gpointer
599 : 3 : g_async_queue_timed_pop_unlocked (GAsyncQueue *queue,
600 : : GTimeVal *end_time)
601 : : {
602 : : gint64 m_end_time;
603 : :
604 : 3 : g_return_val_if_fail (queue, NULL);
605 : :
606 [ + + ]: 2 : if (end_time != NULL)
607 : : {
608 : 1 : m_end_time = g_get_monotonic_time () +
609 : 1 : ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
610 : : }
611 : : else
612 : 1 : m_end_time = -1;
613 : :
614 : 2 : return g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
615 : : }
616 : : G_GNUC_END_IGNORE_DEPRECATIONS
617 : :
618 : : /**
619 : : * g_async_queue_length:
620 : : * @queue: a #GAsyncQueue.
621 : : *
622 : : * Returns the length of the queue.
623 : : *
624 : : * Actually this function returns the number of data items in
625 : : * the queue minus the number of waiting threads, so a negative
626 : : * value means waiting threads, and a positive value means available
627 : : * entries in the @queue. A return value of 0 could mean n entries
628 : : * in the queue and n threads waiting. This can happen due to locking
629 : : * of the queue or due to scheduling.
630 : : *
631 : : * Returns: the length of the @queue
632 : : */
633 : : gint
634 : 3749 : g_async_queue_length (GAsyncQueue *queue)
635 : : {
636 : : gint retval;
637 : :
638 : 3749 : g_return_val_if_fail (queue, 0);
639 : :
640 : 3748 : g_mutex_lock (&queue->mutex);
641 : 3748 : retval = queue->queue.length - queue->waiting_threads;
642 : 3748 : g_mutex_unlock (&queue->mutex);
643 : :
644 : 3748 : return retval;
645 : : }
646 : :
647 : : /**
648 : : * g_async_queue_length_unlocked:
649 : : * @queue: a #GAsyncQueue
650 : : *
651 : : * Returns the length of the queue.
652 : : *
653 : : * Actually this function returns the number of data items in
654 : : * the queue minus the number of waiting threads, so a negative
655 : : * value means waiting threads, and a positive value means available
656 : : * entries in the @queue. A return value of 0 could mean n entries
657 : : * in the queue and n threads waiting. This can happen due to locking
658 : : * of the queue or due to scheduling.
659 : : *
660 : : * This function must be called while holding the @queue's lock.
661 : : *
662 : : * Returns: the length of the @queue.
663 : : */
664 : : gint
665 : 131669 : g_async_queue_length_unlocked (GAsyncQueue *queue)
666 : : {
667 : 131669 : g_return_val_if_fail (queue, 0);
668 : :
669 : 131668 : return queue->queue.length - queue->waiting_threads;
670 : : }
671 : :
672 : : /**
673 : : * g_async_queue_sort:
674 : : * @queue: a #GAsyncQueue
675 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
676 : : * @user_data: user data passed to @func
677 : : *
678 : : * Sorts @queue using @func.
679 : : *
680 : : * The sort function @func is passed two elements of the @queue.
681 : : * It should return 0 if they are equal, a negative value if the
682 : : * first element should be higher in the @queue or a positive value
683 : : * if the first element should be lower in the @queue than the second
684 : : * element.
685 : : *
686 : : * This function will lock @queue before it sorts the queue and unlock
687 : : * it when it is finished.
688 : : *
689 : : * If you were sorting a list of priority numbers to make sure the
690 : : * lowest priority would be at the top of the queue, you could use:
691 : : * |[<!-- language="C" -->
692 : : * gint32 id1;
693 : : * gint32 id2;
694 : : *
695 : : * id1 = GPOINTER_TO_INT (element1);
696 : : * id2 = GPOINTER_TO_INT (element2);
697 : : *
698 : : * return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
699 : : * ]|
700 : : *
701 : : * Since: 2.10
702 : : */
703 : : void
704 : 3 : g_async_queue_sort (GAsyncQueue *queue,
705 : : GCompareDataFunc func,
706 : : gpointer user_data)
707 : : {
708 : 3 : g_return_if_fail (queue != NULL);
709 : 2 : g_return_if_fail (func != NULL);
710 : :
711 : 1 : g_mutex_lock (&queue->mutex);
712 : 1 : g_async_queue_sort_unlocked (queue, func, user_data);
713 : 1 : g_mutex_unlock (&queue->mutex);
714 : : }
715 : :
716 : : /**
717 : : * g_async_queue_sort_unlocked:
718 : : * @queue: a #GAsyncQueue
719 : : * @func: (scope call): the #GCompareDataFunc is used to sort @queue
720 : : * @user_data: user data passed to @func
721 : : *
722 : : * Sorts @queue using @func.
723 : : *
724 : : * The sort function @func is passed two elements of the @queue.
725 : : * It should return 0 if they are equal, a negative value if the
726 : : * first element should be higher in the @queue or a positive value
727 : : * if the first element should be lower in the @queue than the second
728 : : * element.
729 : : *
730 : : * This function must be called while holding the @queue's lock.
731 : : *
732 : : * Since: 2.10
733 : : */
734 : : void
735 : 166 : g_async_queue_sort_unlocked (GAsyncQueue *queue,
736 : : GCompareDataFunc func,
737 : : gpointer user_data)
738 : : {
739 : : SortData sd;
740 : :
741 : 167 : g_return_if_fail (queue != NULL);
742 : 165 : g_return_if_fail (func != NULL);
743 : :
744 : 164 : sd.func = func;
745 : 164 : sd.user_data = user_data;
746 : :
747 : 164 : g_queue_sort (&queue->queue,
748 : : (GCompareDataFunc)g_async_queue_invert_compare,
749 : : &sd);
750 : : }
751 : :
752 : : /**
753 : : * g_async_queue_remove:
754 : : * @queue: a #GAsyncQueue
755 : : * @item: (not nullable): the data to remove from the @queue
756 : : *
757 : : * Remove an item from the queue.
758 : : *
759 : : * Returns: %TRUE if the item was removed
760 : : *
761 : : * Since: 2.46
762 : : */
763 : : gboolean
764 : 50 : g_async_queue_remove (GAsyncQueue *queue,
765 : : gpointer item)
766 : : {
767 : : gboolean ret;
768 : :
769 : 50 : g_return_val_if_fail (queue != NULL, FALSE);
770 : 49 : g_return_val_if_fail (item != NULL, FALSE);
771 : :
772 : 48 : g_mutex_lock (&queue->mutex);
773 : 48 : ret = g_async_queue_remove_unlocked (queue, item);
774 : 48 : g_mutex_unlock (&queue->mutex);
775 : :
776 : 48 : return ret;
777 : : }
778 : :
779 : : /**
780 : : * g_async_queue_remove_unlocked:
781 : : * @queue: a #GAsyncQueue
782 : : * @item: the data to remove from the @queue
783 : : *
784 : : * Remove an item from the queue.
785 : : *
786 : : * This function must be called while holding the @queue's lock.
787 : : *
788 : : * Returns: %TRUE if the item was removed
789 : : *
790 : : * Since: 2.46
791 : : */
792 : : gboolean
793 : 1087 : g_async_queue_remove_unlocked (GAsyncQueue *queue,
794 : : gpointer item)
795 : : {
796 : 1087 : g_return_val_if_fail (queue != NULL, FALSE);
797 : 1086 : g_return_val_if_fail (item != NULL, FALSE);
798 : :
799 : 1085 : return g_queue_remove (&queue->queue, item);
800 : : }
801 : :
802 : : /**
803 : : * g_async_queue_push_front:
804 : : * @queue: a #GAsyncQueue
805 : : * @item: (not nullable): data to push into the @queue
806 : : *
807 : : * Pushes the @item into the @queue. @item must not be %NULL.
808 : : * In contrast to g_async_queue_push(), this function
809 : : * pushes the new item ahead of the items already in the queue,
810 : : * so that it will be the next one to be popped off the queue.
811 : : *
812 : : * Since: 2.46
813 : : */
814 : : void
815 : 3 : g_async_queue_push_front (GAsyncQueue *queue,
816 : : gpointer item)
817 : : {
818 : 3 : g_return_if_fail (queue != NULL);
819 : 2 : g_return_if_fail (item != NULL);
820 : :
821 : 1 : g_mutex_lock (&queue->mutex);
822 : 1 : g_async_queue_push_front_unlocked (queue, item);
823 : 1 : g_mutex_unlock (&queue->mutex);
824 : : }
825 : :
826 : : /**
827 : : * g_async_queue_push_front_unlocked:
828 : : * @queue: a #GAsyncQueue
829 : : * @item: (not nullable): data to push into the @queue
830 : : *
831 : : * Pushes the @item into the @queue. @item must not be %NULL.
832 : : * In contrast to g_async_queue_push_unlocked(), this function
833 : : * pushes the new item ahead of the items already in the queue,
834 : : * so that it will be the next one to be popped off the queue.
835 : : *
836 : : * This function must be called while holding the @queue's lock.
837 : : *
838 : : * Since: 2.46
839 : : */
840 : : void
841 : 1017 : g_async_queue_push_front_unlocked (GAsyncQueue *queue,
842 : : gpointer item)
843 : : {
844 : 1017 : g_return_if_fail (queue != NULL);
845 : 1016 : g_return_if_fail (item != NULL);
846 : :
847 : 1015 : g_queue_push_tail (&queue->queue, item);
848 [ + + ]: 1015 : if (queue->waiting_threads > 0)
849 : 3 : g_cond_signal (&queue->cond);
850 : : }
851 : :
852 : : /*
853 : : * Private API
854 : : */
855 : :
856 : : GMutex *
857 : 369 : _g_async_queue_get_mutex (GAsyncQueue *queue)
858 : : {
859 : 369 : g_return_val_if_fail (queue, NULL);
860 : :
861 : 369 : return &queue->mutex;
862 : : }
|