/src/rauc/subprojects/glib-2.76.5/glib/gasyncqueue.c
Line | Count | Source (jump to first uncovered line) |
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 | | /** |
42 | | * SECTION:async_queues |
43 | | * @title: Asynchronous Queues |
44 | | * @short_description: asynchronous communication between threads |
45 | | * @see_also: #GThreadPool |
46 | | * |
47 | | * Often you need to communicate between different threads. In general |
48 | | * it's safer not to do this by shared memory, but by explicit message |
49 | | * passing. These messages only make sense asynchronously for |
50 | | * multi-threaded applications though, as a synchronous operation could |
51 | | * as well be done in the same thread. |
52 | | * |
53 | | * Asynchronous queues are an exception from most other GLib data |
54 | | * structures, as they can be used simultaneously from multiple threads |
55 | | * without explicit locking and they bring their own builtin reference |
56 | | * counting. This is because the nature of an asynchronous queue is that |
57 | | * it will always be used by at least 2 concurrent threads. |
58 | | * |
59 | | * For using an asynchronous queue you first have to create one with |
60 | | * g_async_queue_new(). #GAsyncQueue structs are reference counted, |
61 | | * use g_async_queue_ref() and g_async_queue_unref() to manage your |
62 | | * references. |
63 | | * |
64 | | * A thread which wants to send a message to that queue simply calls |
65 | | * g_async_queue_push() to push the message to the queue. |
66 | | * |
67 | | * A thread which is expecting messages from an asynchronous queue |
68 | | * simply calls g_async_queue_pop() for that queue. If no message is |
69 | | * available in the queue at that point, the thread is now put to sleep |
70 | | * until a message arrives. The message will be removed from the queue |
71 | | * and returned. The functions g_async_queue_try_pop() and |
72 | | * g_async_queue_timeout_pop() can be used to only check for the presence |
73 | | * of messages or to only wait a certain time for messages respectively. |
74 | | * |
75 | | * For almost every function there exist two variants, one that locks |
76 | | * the queue and one that doesn't. That way you can hold the queue lock |
77 | | * (acquire it with g_async_queue_lock() and release it with |
78 | | * g_async_queue_unlock()) over multiple queue accessing instructions. |
79 | | * This can be necessary to ensure the integrity of the queue, but should |
80 | | * only be used when really necessary, as it can make your life harder |
81 | | * if used unwisely. Normally you should only use the locking function |
82 | | * variants (those without the _unlocked suffix). |
83 | | * |
84 | | * In many cases, it may be more convenient to use #GThreadPool when |
85 | | * you need to distribute work to a set of worker threads instead of |
86 | | * using #GAsyncQueue manually. #GThreadPool uses a GAsyncQueue |
87 | | * internally. |
88 | | */ |
89 | | |
90 | | /** |
91 | | * GAsyncQueue: |
92 | | * |
93 | | * An opaque data structure which represents an asynchronous queue. |
94 | | * |
95 | | * It should only be accessed through the `g_async_queue_*` functions. |
96 | | */ |
97 | | struct _GAsyncQueue |
98 | | { |
99 | | GMutex mutex; |
100 | | GCond cond; |
101 | | GQueue queue; |
102 | | GDestroyNotify item_free_func; |
103 | | guint waiting_threads; |
104 | | gint ref_count; |
105 | | }; |
106 | | |
107 | | typedef struct |
108 | | { |
109 | | GCompareDataFunc func; |
110 | | gpointer user_data; |
111 | | } SortData; |
112 | | |
113 | | /** |
114 | | * g_async_queue_new: |
115 | | * |
116 | | * Creates a new asynchronous queue. |
117 | | * |
118 | | * Returns: a new #GAsyncQueue. Free with g_async_queue_unref() |
119 | | */ |
120 | | GAsyncQueue * |
121 | | g_async_queue_new (void) |
122 | 0 | { |
123 | 0 | return g_async_queue_new_full (NULL); |
124 | 0 | } |
125 | | |
126 | | /** |
127 | | * g_async_queue_new_full: |
128 | | * @item_free_func: (nullable): function to free queue elements |
129 | | * |
130 | | * Creates a new asynchronous queue and sets up a destroy notify |
131 | | * function that is used to free any remaining queue items when |
132 | | * the queue is destroyed after the final unref. |
133 | | * |
134 | | * Returns: a new #GAsyncQueue. Free with g_async_queue_unref() |
135 | | * |
136 | | * Since: 2.16 |
137 | | */ |
138 | | GAsyncQueue * |
139 | | g_async_queue_new_full (GDestroyNotify item_free_func) |
140 | 0 | { |
141 | 0 | GAsyncQueue *queue; |
142 | |
|
143 | 0 | queue = g_new (GAsyncQueue, 1); |
144 | 0 | g_mutex_init (&queue->mutex); |
145 | 0 | g_cond_init (&queue->cond); |
146 | 0 | g_queue_init (&queue->queue); |
147 | 0 | queue->waiting_threads = 0; |
148 | 0 | queue->ref_count = 1; |
149 | 0 | queue->item_free_func = item_free_func; |
150 | |
|
151 | 0 | return queue; |
152 | 0 | } |
153 | | |
154 | | /** |
155 | | * g_async_queue_ref: |
156 | | * @queue: a #GAsyncQueue |
157 | | * |
158 | | * Increases the reference count of the asynchronous @queue by 1. |
159 | | * You do not need to hold the lock to call this function. |
160 | | * |
161 | | * Returns: the @queue that was passed in (since 2.6) |
162 | | */ |
163 | | GAsyncQueue * |
164 | | g_async_queue_ref (GAsyncQueue *queue) |
165 | 0 | { |
166 | 0 | g_return_val_if_fail (queue, NULL); |
167 | | |
168 | 0 | g_atomic_int_inc (&queue->ref_count); |
169 | |
|
170 | 0 | return queue; |
171 | 0 | } |
172 | | |
173 | | /** |
174 | | * g_async_queue_ref_unlocked: |
175 | | * @queue: a #GAsyncQueue |
176 | | * |
177 | | * Increases the reference count of the asynchronous @queue by 1. |
178 | | * |
179 | | * Deprecated: 2.8: Reference counting is done atomically. |
180 | | * so g_async_queue_ref() can be used regardless of the @queue's |
181 | | * lock. |
182 | | */ |
183 | | void |
184 | | g_async_queue_ref_unlocked (GAsyncQueue *queue) |
185 | 0 | { |
186 | 0 | g_return_if_fail (queue); |
187 | | |
188 | 0 | g_atomic_int_inc (&queue->ref_count); |
189 | 0 | } |
190 | | |
191 | | /** |
192 | | * g_async_queue_unref_and_unlock: |
193 | | * @queue: a #GAsyncQueue |
194 | | * |
195 | | * Decreases the reference count of the asynchronous @queue by 1 |
196 | | * and releases the lock. This function must be called while holding |
197 | | * the @queue's lock. If the reference count went to 0, the @queue |
198 | | * will be destroyed and the memory allocated will be freed. |
199 | | * |
200 | | * Deprecated: 2.8: Reference counting is done atomically. |
201 | | * so g_async_queue_unref() can be used regardless of the @queue's |
202 | | * lock. |
203 | | */ |
204 | | void |
205 | | g_async_queue_unref_and_unlock (GAsyncQueue *queue) |
206 | 0 | { |
207 | 0 | g_return_if_fail (queue); |
208 | | |
209 | 0 | g_mutex_unlock (&queue->mutex); |
210 | 0 | g_async_queue_unref (queue); |
211 | 0 | } |
212 | | |
213 | | /** |
214 | | * g_async_queue_unref: |
215 | | * @queue: a #GAsyncQueue. |
216 | | * |
217 | | * Decreases the reference count of the asynchronous @queue by 1. |
218 | | * |
219 | | * If the reference count went to 0, the @queue will be destroyed |
220 | | * and the memory allocated will be freed. So you are not allowed |
221 | | * to use the @queue afterwards, as it might have disappeared. |
222 | | * You do not need to hold the lock to call this function. |
223 | | */ |
224 | | void |
225 | | g_async_queue_unref (GAsyncQueue *queue) |
226 | 0 | { |
227 | 0 | g_return_if_fail (queue); |
228 | | |
229 | 0 | if (g_atomic_int_dec_and_test (&queue->ref_count)) |
230 | 0 | { |
231 | 0 | g_return_if_fail (queue->waiting_threads == 0); |
232 | 0 | g_mutex_clear (&queue->mutex); |
233 | 0 | g_cond_clear (&queue->cond); |
234 | 0 | if (queue->item_free_func) |
235 | 0 | g_queue_foreach (&queue->queue, (GFunc) queue->item_free_func, NULL); |
236 | 0 | g_queue_clear (&queue->queue); |
237 | 0 | g_free (queue); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | /** |
242 | | * g_async_queue_lock: |
243 | | * @queue: a #GAsyncQueue |
244 | | * |
245 | | * Acquires the @queue's lock. If another thread is already |
246 | | * holding the lock, this call will block until the lock |
247 | | * becomes available. |
248 | | * |
249 | | * Call g_async_queue_unlock() to drop the lock again. |
250 | | * |
251 | | * While holding the lock, you can only call the |
252 | | * g_async_queue_*_unlocked() functions on @queue. Otherwise, |
253 | | * deadlock may occur. |
254 | | */ |
255 | | void |
256 | | g_async_queue_lock (GAsyncQueue *queue) |
257 | 0 | { |
258 | 0 | g_return_if_fail (queue); |
259 | | |
260 | 0 | g_mutex_lock (&queue->mutex); |
261 | 0 | } |
262 | | |
263 | | /** |
264 | | * g_async_queue_unlock: |
265 | | * @queue: a #GAsyncQueue |
266 | | * |
267 | | * Releases the queue's lock. |
268 | | * |
269 | | * Calling this function when you have not acquired |
270 | | * the with g_async_queue_lock() leads to undefined |
271 | | * behaviour. |
272 | | */ |
273 | | void |
274 | | g_async_queue_unlock (GAsyncQueue *queue) |
275 | 0 | { |
276 | 0 | g_return_if_fail (queue); |
277 | | |
278 | 0 | g_mutex_unlock (&queue->mutex); |
279 | 0 | } |
280 | | |
281 | | /** |
282 | | * g_async_queue_push: |
283 | | * @queue: a #GAsyncQueue |
284 | | * @data: (not nullable): data to push onto the @queue |
285 | | * |
286 | | * Pushes the @data into the @queue. |
287 | | * |
288 | | * The @data parameter must not be %NULL. |
289 | | */ |
290 | | void |
291 | | g_async_queue_push (GAsyncQueue *queue, |
292 | | gpointer data) |
293 | 0 | { |
294 | 0 | g_return_if_fail (queue); |
295 | 0 | g_return_if_fail (data); |
296 | | |
297 | 0 | g_mutex_lock (&queue->mutex); |
298 | 0 | g_async_queue_push_unlocked (queue, data); |
299 | 0 | g_mutex_unlock (&queue->mutex); |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * g_async_queue_push_unlocked: |
304 | | * @queue: a #GAsyncQueue |
305 | | * @data: (not nullable): data to push onto the @queue |
306 | | * |
307 | | * Pushes the @data into the @queue. |
308 | | * |
309 | | * The @data parameter must not be %NULL. |
310 | | * |
311 | | * This function must be called while holding the @queue's lock. |
312 | | */ |
313 | | void |
314 | | g_async_queue_push_unlocked (GAsyncQueue *queue, |
315 | | gpointer data) |
316 | 0 | { |
317 | 0 | g_return_if_fail (queue); |
318 | 0 | g_return_if_fail (data); |
319 | | |
320 | 0 | g_queue_push_head (&queue->queue, data); |
321 | 0 | if (queue->waiting_threads > 0) |
322 | 0 | g_cond_signal (&queue->cond); |
323 | 0 | } |
324 | | |
325 | | /** |
326 | | * g_async_queue_push_sorted: |
327 | | * @queue: a #GAsyncQueue |
328 | | * @data: (not nullable): the @data to push into the @queue |
329 | | * @func: the #GCompareDataFunc is used to sort @queue |
330 | | * @user_data: user data passed to @func. |
331 | | * |
332 | | * Inserts @data into @queue using @func to determine the new |
333 | | * position. |
334 | | * |
335 | | * This function requires that the @queue is sorted before pushing on |
336 | | * new elements, see g_async_queue_sort(). |
337 | | * |
338 | | * This function will lock @queue before it sorts the queue and unlock |
339 | | * it when it is finished. |
340 | | * |
341 | | * For an example of @func see g_async_queue_sort(). |
342 | | * |
343 | | * Since: 2.10 |
344 | | */ |
345 | | void |
346 | | g_async_queue_push_sorted (GAsyncQueue *queue, |
347 | | gpointer data, |
348 | | GCompareDataFunc func, |
349 | | gpointer user_data) |
350 | 0 | { |
351 | 0 | g_return_if_fail (queue != NULL); |
352 | | |
353 | 0 | g_mutex_lock (&queue->mutex); |
354 | 0 | g_async_queue_push_sorted_unlocked (queue, data, func, user_data); |
355 | 0 | g_mutex_unlock (&queue->mutex); |
356 | 0 | } |
357 | | |
358 | | static gint |
359 | | g_async_queue_invert_compare (gpointer v1, |
360 | | gpointer v2, |
361 | | SortData *sd) |
362 | 0 | { |
363 | 0 | return -sd->func (v1, v2, sd->user_data); |
364 | 0 | } |
365 | | |
366 | | /** |
367 | | * g_async_queue_push_sorted_unlocked: |
368 | | * @queue: a #GAsyncQueue |
369 | | * @data: the data to push into the @queue |
370 | | * @func: the #GCompareDataFunc is used to sort @queue |
371 | | * @user_data: user data passed to @func. |
372 | | * |
373 | | * Inserts @data into @queue using @func to determine the new |
374 | | * position. |
375 | | * |
376 | | * The sort function @func is passed two elements of the @queue. |
377 | | * It should return 0 if they are equal, a negative value if the |
378 | | * first element should be higher in the @queue or a positive value |
379 | | * if the first element should be lower in the @queue than the second |
380 | | * element. |
381 | | * |
382 | | * This function requires that the @queue is sorted before pushing on |
383 | | * new elements, see g_async_queue_sort(). |
384 | | * |
385 | | * This function must be called while holding the @queue's lock. |
386 | | * |
387 | | * For an example of @func see g_async_queue_sort(). |
388 | | * |
389 | | * Since: 2.10 |
390 | | */ |
391 | | void |
392 | | g_async_queue_push_sorted_unlocked (GAsyncQueue *queue, |
393 | | gpointer data, |
394 | | GCompareDataFunc func, |
395 | | gpointer user_data) |
396 | 0 | { |
397 | 0 | SortData sd; |
398 | |
|
399 | 0 | g_return_if_fail (queue != NULL); |
400 | | |
401 | 0 | sd.func = func; |
402 | 0 | sd.user_data = user_data; |
403 | |
|
404 | 0 | g_queue_insert_sorted (&queue->queue, |
405 | 0 | data, |
406 | 0 | (GCompareDataFunc)g_async_queue_invert_compare, |
407 | 0 | &sd); |
408 | 0 | if (queue->waiting_threads > 0) |
409 | 0 | g_cond_signal (&queue->cond); |
410 | 0 | } |
411 | | |
412 | | static gpointer |
413 | | g_async_queue_pop_intern_unlocked (GAsyncQueue *queue, |
414 | | gboolean wait, |
415 | | gint64 end_time) |
416 | 0 | { |
417 | 0 | gpointer retval; |
418 | |
|
419 | 0 | if (!g_queue_peek_tail_link (&queue->queue) && wait) |
420 | 0 | { |
421 | 0 | queue->waiting_threads++; |
422 | 0 | while (!g_queue_peek_tail_link (&queue->queue)) |
423 | 0 | { |
424 | 0 | if (end_time == -1) |
425 | 0 | g_cond_wait (&queue->cond, &queue->mutex); |
426 | 0 | else |
427 | 0 | { |
428 | 0 | if (!g_cond_wait_until (&queue->cond, &queue->mutex, end_time)) |
429 | 0 | break; |
430 | 0 | } |
431 | 0 | } |
432 | 0 | queue->waiting_threads--; |
433 | 0 | } |
434 | |
|
435 | 0 | retval = g_queue_pop_tail (&queue->queue); |
436 | |
|
437 | 0 | g_assert (retval || !wait || end_time > 0); |
438 | | |
439 | 0 | return retval; |
440 | 0 | } |
441 | | |
442 | | /** |
443 | | * g_async_queue_pop: |
444 | | * @queue: a #GAsyncQueue |
445 | | * |
446 | | * Pops data from the @queue. If @queue is empty, this function |
447 | | * blocks until data becomes available. |
448 | | * |
449 | | * Returns: data from the queue |
450 | | */ |
451 | | gpointer |
452 | | g_async_queue_pop (GAsyncQueue *queue) |
453 | 0 | { |
454 | 0 | gpointer retval; |
455 | |
|
456 | 0 | g_return_val_if_fail (queue, NULL); |
457 | | |
458 | 0 | g_mutex_lock (&queue->mutex); |
459 | 0 | retval = g_async_queue_pop_intern_unlocked (queue, TRUE, -1); |
460 | 0 | g_mutex_unlock (&queue->mutex); |
461 | |
|
462 | 0 | return retval; |
463 | 0 | } |
464 | | |
465 | | /** |
466 | | * g_async_queue_pop_unlocked: |
467 | | * @queue: a #GAsyncQueue |
468 | | * |
469 | | * Pops data from the @queue. If @queue is empty, this function |
470 | | * blocks until data becomes available. |
471 | | * |
472 | | * This function must be called while holding the @queue's lock. |
473 | | * |
474 | | * Returns: data from the queue. |
475 | | */ |
476 | | gpointer |
477 | | g_async_queue_pop_unlocked (GAsyncQueue *queue) |
478 | 0 | { |
479 | 0 | g_return_val_if_fail (queue, NULL); |
480 | | |
481 | 0 | return g_async_queue_pop_intern_unlocked (queue, TRUE, -1); |
482 | 0 | } |
483 | | |
484 | | /** |
485 | | * g_async_queue_try_pop: |
486 | | * @queue: a #GAsyncQueue |
487 | | * |
488 | | * Tries to pop data from the @queue. If no data is available, |
489 | | * %NULL is returned. |
490 | | * |
491 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
492 | | * available immediately. |
493 | | */ |
494 | | gpointer |
495 | | g_async_queue_try_pop (GAsyncQueue *queue) |
496 | 0 | { |
497 | 0 | gpointer retval; |
498 | |
|
499 | 0 | g_return_val_if_fail (queue, NULL); |
500 | | |
501 | 0 | g_mutex_lock (&queue->mutex); |
502 | 0 | retval = g_async_queue_pop_intern_unlocked (queue, FALSE, -1); |
503 | 0 | g_mutex_unlock (&queue->mutex); |
504 | |
|
505 | 0 | return retval; |
506 | 0 | } |
507 | | |
508 | | /** |
509 | | * g_async_queue_try_pop_unlocked: |
510 | | * @queue: a #GAsyncQueue |
511 | | * |
512 | | * Tries to pop data from the @queue. If no data is available, |
513 | | * %NULL is returned. |
514 | | * |
515 | | * This function must be called while holding the @queue's lock. |
516 | | * |
517 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
518 | | * available immediately. |
519 | | */ |
520 | | gpointer |
521 | | g_async_queue_try_pop_unlocked (GAsyncQueue *queue) |
522 | 0 | { |
523 | 0 | g_return_val_if_fail (queue, NULL); |
524 | | |
525 | 0 | return g_async_queue_pop_intern_unlocked (queue, FALSE, -1); |
526 | 0 | } |
527 | | |
528 | | /** |
529 | | * g_async_queue_timeout_pop: |
530 | | * @queue: a #GAsyncQueue |
531 | | * @timeout: the number of microseconds to wait |
532 | | * |
533 | | * Pops data from the @queue. If the queue is empty, blocks for |
534 | | * @timeout microseconds, or until data becomes available. |
535 | | * |
536 | | * If no data is received before the timeout, %NULL is returned. |
537 | | * |
538 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
539 | | * received before the timeout. |
540 | | */ |
541 | | gpointer |
542 | | g_async_queue_timeout_pop (GAsyncQueue *queue, |
543 | | guint64 timeout) |
544 | 0 | { |
545 | 0 | gint64 end_time = g_get_monotonic_time () + timeout; |
546 | 0 | gpointer retval; |
547 | |
|
548 | 0 | g_return_val_if_fail (queue != NULL, NULL); |
549 | | |
550 | 0 | g_mutex_lock (&queue->mutex); |
551 | 0 | retval = g_async_queue_pop_intern_unlocked (queue, TRUE, end_time); |
552 | 0 | g_mutex_unlock (&queue->mutex); |
553 | |
|
554 | 0 | return retval; |
555 | 0 | } |
556 | | |
557 | | /** |
558 | | * g_async_queue_timeout_pop_unlocked: |
559 | | * @queue: a #GAsyncQueue |
560 | | * @timeout: the number of microseconds to wait |
561 | | * |
562 | | * Pops data from the @queue. If the queue is empty, blocks for |
563 | | * @timeout microseconds, or until data becomes available. |
564 | | * |
565 | | * If no data is received before the timeout, %NULL is returned. |
566 | | * |
567 | | * This function must be called while holding the @queue's lock. |
568 | | * |
569 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
570 | | * received before the timeout. |
571 | | */ |
572 | | gpointer |
573 | | g_async_queue_timeout_pop_unlocked (GAsyncQueue *queue, |
574 | | guint64 timeout) |
575 | 0 | { |
576 | 0 | gint64 end_time = g_get_monotonic_time () + timeout; |
577 | |
|
578 | 0 | g_return_val_if_fail (queue != NULL, NULL); |
579 | | |
580 | 0 | return g_async_queue_pop_intern_unlocked (queue, TRUE, end_time); |
581 | 0 | } |
582 | | |
583 | | /** |
584 | | * g_async_queue_timed_pop: |
585 | | * @queue: a #GAsyncQueue |
586 | | * @end_time: a #GTimeVal, determining the final time |
587 | | * |
588 | | * Pops data from the @queue. If the queue is empty, blocks until |
589 | | * @end_time or until data becomes available. |
590 | | * |
591 | | * If no data is received before @end_time, %NULL is returned. |
592 | | * |
593 | | * To easily calculate @end_time, a combination of g_get_real_time() |
594 | | * and g_time_val_add() can be used. |
595 | | * |
596 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
597 | | * received before @end_time. |
598 | | * |
599 | | * Deprecated: use g_async_queue_timeout_pop(). |
600 | | */ |
601 | | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
602 | | gpointer |
603 | | g_async_queue_timed_pop (GAsyncQueue *queue, |
604 | | GTimeVal *end_time) |
605 | 0 | { |
606 | 0 | gint64 m_end_time; |
607 | 0 | gpointer retval; |
608 | |
|
609 | 0 | g_return_val_if_fail (queue, NULL); |
610 | | |
611 | 0 | if (end_time != NULL) |
612 | 0 | { |
613 | 0 | m_end_time = g_get_monotonic_time () + |
614 | 0 | ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ()); |
615 | 0 | } |
616 | 0 | else |
617 | 0 | m_end_time = -1; |
618 | |
|
619 | 0 | g_mutex_lock (&queue->mutex); |
620 | 0 | retval = g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time); |
621 | 0 | g_mutex_unlock (&queue->mutex); |
622 | |
|
623 | 0 | return retval; |
624 | 0 | } |
625 | | G_GNUC_END_IGNORE_DEPRECATIONS |
626 | | |
627 | | /** |
628 | | * g_async_queue_timed_pop_unlocked: |
629 | | * @queue: a #GAsyncQueue |
630 | | * @end_time: a #GTimeVal, determining the final time |
631 | | * |
632 | | * Pops data from the @queue. If the queue is empty, blocks until |
633 | | * @end_time or until data becomes available. |
634 | | * |
635 | | * If no data is received before @end_time, %NULL is returned. |
636 | | * |
637 | | * To easily calculate @end_time, a combination of g_get_real_time() |
638 | | * and g_time_val_add() can be used. |
639 | | * |
640 | | * This function must be called while holding the @queue's lock. |
641 | | * |
642 | | * Returns: (nullable): data from the queue or %NULL, when no data is |
643 | | * received before @end_time. |
644 | | * |
645 | | * Deprecated: use g_async_queue_timeout_pop_unlocked(). |
646 | | */ |
647 | | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
648 | | gpointer |
649 | | g_async_queue_timed_pop_unlocked (GAsyncQueue *queue, |
650 | | GTimeVal *end_time) |
651 | 0 | { |
652 | 0 | gint64 m_end_time; |
653 | |
|
654 | 0 | g_return_val_if_fail (queue, NULL); |
655 | | |
656 | 0 | if (end_time != NULL) |
657 | 0 | { |
658 | 0 | m_end_time = g_get_monotonic_time () + |
659 | 0 | ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ()); |
660 | 0 | } |
661 | 0 | else |
662 | 0 | m_end_time = -1; |
663 | |
|
664 | 0 | return g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time); |
665 | 0 | } |
666 | | G_GNUC_END_IGNORE_DEPRECATIONS |
667 | | |
668 | | /** |
669 | | * g_async_queue_length: |
670 | | * @queue: a #GAsyncQueue. |
671 | | * |
672 | | * Returns the length of the queue. |
673 | | * |
674 | | * Actually this function returns the number of data items in |
675 | | * the queue minus the number of waiting threads, so a negative |
676 | | * value means waiting threads, and a positive value means available |
677 | | * entries in the @queue. A return value of 0 could mean n entries |
678 | | * in the queue and n threads waiting. This can happen due to locking |
679 | | * of the queue or due to scheduling. |
680 | | * |
681 | | * Returns: the length of the @queue |
682 | | */ |
683 | | gint |
684 | | g_async_queue_length (GAsyncQueue *queue) |
685 | 0 | { |
686 | 0 | gint retval; |
687 | |
|
688 | 0 | g_return_val_if_fail (queue, 0); |
689 | | |
690 | 0 | g_mutex_lock (&queue->mutex); |
691 | 0 | retval = queue->queue.length - queue->waiting_threads; |
692 | 0 | g_mutex_unlock (&queue->mutex); |
693 | |
|
694 | 0 | return retval; |
695 | 0 | } |
696 | | |
697 | | /** |
698 | | * g_async_queue_length_unlocked: |
699 | | * @queue: a #GAsyncQueue |
700 | | * |
701 | | * Returns the length of the queue. |
702 | | * |
703 | | * Actually this function returns the number of data items in |
704 | | * the queue minus the number of waiting threads, so a negative |
705 | | * value means waiting threads, and a positive value means available |
706 | | * entries in the @queue. A return value of 0 could mean n entries |
707 | | * in the queue and n threads waiting. This can happen due to locking |
708 | | * of the queue or due to scheduling. |
709 | | * |
710 | | * This function must be called while holding the @queue's lock. |
711 | | * |
712 | | * Returns: the length of the @queue. |
713 | | */ |
714 | | gint |
715 | | g_async_queue_length_unlocked (GAsyncQueue *queue) |
716 | 0 | { |
717 | 0 | g_return_val_if_fail (queue, 0); |
718 | | |
719 | 0 | return queue->queue.length - queue->waiting_threads; |
720 | 0 | } |
721 | | |
722 | | /** |
723 | | * g_async_queue_sort: |
724 | | * @queue: a #GAsyncQueue |
725 | | * @func: the #GCompareDataFunc is used to sort @queue |
726 | | * @user_data: user data passed to @func |
727 | | * |
728 | | * Sorts @queue using @func. |
729 | | * |
730 | | * The sort function @func is passed two elements of the @queue. |
731 | | * It should return 0 if they are equal, a negative value if the |
732 | | * first element should be higher in the @queue or a positive value |
733 | | * if the first element should be lower in the @queue than the second |
734 | | * element. |
735 | | * |
736 | | * This function will lock @queue before it sorts the queue and unlock |
737 | | * it when it is finished. |
738 | | * |
739 | | * If you were sorting a list of priority numbers to make sure the |
740 | | * lowest priority would be at the top of the queue, you could use: |
741 | | * |[<!-- language="C" --> |
742 | | * gint32 id1; |
743 | | * gint32 id2; |
744 | | * |
745 | | * id1 = GPOINTER_TO_INT (element1); |
746 | | * id2 = GPOINTER_TO_INT (element2); |
747 | | * |
748 | | * return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1); |
749 | | * ]| |
750 | | * |
751 | | * Since: 2.10 |
752 | | */ |
753 | | void |
754 | | g_async_queue_sort (GAsyncQueue *queue, |
755 | | GCompareDataFunc func, |
756 | | gpointer user_data) |
757 | 0 | { |
758 | 0 | g_return_if_fail (queue != NULL); |
759 | 0 | g_return_if_fail (func != NULL); |
760 | | |
761 | 0 | g_mutex_lock (&queue->mutex); |
762 | 0 | g_async_queue_sort_unlocked (queue, func, user_data); |
763 | 0 | g_mutex_unlock (&queue->mutex); |
764 | 0 | } |
765 | | |
766 | | /** |
767 | | * g_async_queue_sort_unlocked: |
768 | | * @queue: a #GAsyncQueue |
769 | | * @func: the #GCompareDataFunc is used to sort @queue |
770 | | * @user_data: user data passed to @func |
771 | | * |
772 | | * Sorts @queue using @func. |
773 | | * |
774 | | * The sort function @func is passed two elements of the @queue. |
775 | | * It should return 0 if they are equal, a negative value if the |
776 | | * first element should be higher in the @queue or a positive value |
777 | | * if the first element should be lower in the @queue than the second |
778 | | * element. |
779 | | * |
780 | | * This function must be called while holding the @queue's lock. |
781 | | * |
782 | | * Since: 2.10 |
783 | | */ |
784 | | void |
785 | | g_async_queue_sort_unlocked (GAsyncQueue *queue, |
786 | | GCompareDataFunc func, |
787 | | gpointer user_data) |
788 | 0 | { |
789 | 0 | SortData sd; |
790 | |
|
791 | 0 | g_return_if_fail (queue != NULL); |
792 | 0 | g_return_if_fail (func != NULL); |
793 | | |
794 | 0 | sd.func = func; |
795 | 0 | sd.user_data = user_data; |
796 | |
|
797 | 0 | g_queue_sort (&queue->queue, |
798 | 0 | (GCompareDataFunc)g_async_queue_invert_compare, |
799 | 0 | &sd); |
800 | 0 | } |
801 | | |
802 | | /** |
803 | | * g_async_queue_remove: |
804 | | * @queue: a #GAsyncQueue |
805 | | * @item: (not nullable): the data to remove from the @queue |
806 | | * |
807 | | * Remove an item from the queue. |
808 | | * |
809 | | * Returns: %TRUE if the item was removed |
810 | | * |
811 | | * Since: 2.46 |
812 | | */ |
813 | | gboolean |
814 | | g_async_queue_remove (GAsyncQueue *queue, |
815 | | gpointer item) |
816 | 0 | { |
817 | 0 | gboolean ret; |
818 | |
|
819 | 0 | g_return_val_if_fail (queue != NULL, FALSE); |
820 | 0 | g_return_val_if_fail (item != NULL, FALSE); |
821 | | |
822 | 0 | g_mutex_lock (&queue->mutex); |
823 | 0 | ret = g_async_queue_remove_unlocked (queue, item); |
824 | 0 | g_mutex_unlock (&queue->mutex); |
825 | |
|
826 | 0 | return ret; |
827 | 0 | } |
828 | | |
829 | | /** |
830 | | * g_async_queue_remove_unlocked: |
831 | | * @queue: a #GAsyncQueue |
832 | | * @item: the data to remove from the @queue |
833 | | * |
834 | | * Remove an item from the queue. |
835 | | * |
836 | | * This function must be called while holding the @queue's lock. |
837 | | * |
838 | | * Returns: %TRUE if the item was removed |
839 | | * |
840 | | * Since: 2.46 |
841 | | */ |
842 | | gboolean |
843 | | g_async_queue_remove_unlocked (GAsyncQueue *queue, |
844 | | gpointer item) |
845 | 0 | { |
846 | 0 | g_return_val_if_fail (queue != NULL, FALSE); |
847 | 0 | g_return_val_if_fail (item != NULL, FALSE); |
848 | | |
849 | 0 | return g_queue_remove (&queue->queue, item); |
850 | 0 | } |
851 | | |
852 | | /** |
853 | | * g_async_queue_push_front: |
854 | | * @queue: a #GAsyncQueue |
855 | | * @item: (not nullable): data to push into the @queue |
856 | | * |
857 | | * Pushes the @item into the @queue. @item must not be %NULL. |
858 | | * In contrast to g_async_queue_push(), this function |
859 | | * pushes the new item ahead of the items already in the queue, |
860 | | * so that it will be the next one to be popped off the queue. |
861 | | * |
862 | | * Since: 2.46 |
863 | | */ |
864 | | void |
865 | | g_async_queue_push_front (GAsyncQueue *queue, |
866 | | gpointer item) |
867 | 0 | { |
868 | 0 | g_return_if_fail (queue != NULL); |
869 | 0 | g_return_if_fail (item != NULL); |
870 | | |
871 | 0 | g_mutex_lock (&queue->mutex); |
872 | 0 | g_async_queue_push_front_unlocked (queue, item); |
873 | 0 | g_mutex_unlock (&queue->mutex); |
874 | 0 | } |
875 | | |
876 | | /** |
877 | | * g_async_queue_push_front_unlocked: |
878 | | * @queue: a #GAsyncQueue |
879 | | * @item: (not nullable): data to push into the @queue |
880 | | * |
881 | | * Pushes the @item into the @queue. @item must not be %NULL. |
882 | | * In contrast to g_async_queue_push_unlocked(), this function |
883 | | * pushes the new item ahead of the items already in the queue, |
884 | | * so that it will be the next one to be popped off the queue. |
885 | | * |
886 | | * This function must be called while holding the @queue's lock. |
887 | | * |
888 | | * Since: 2.46 |
889 | | */ |
890 | | void |
891 | | g_async_queue_push_front_unlocked (GAsyncQueue *queue, |
892 | | gpointer item) |
893 | 0 | { |
894 | 0 | g_return_if_fail (queue != NULL); |
895 | 0 | g_return_if_fail (item != NULL); |
896 | | |
897 | 0 | g_queue_push_tail (&queue->queue, item); |
898 | 0 | if (queue->waiting_threads > 0) |
899 | 0 | g_cond_signal (&queue->cond); |
900 | 0 | } |
901 | | |
902 | | /* |
903 | | * Private API |
904 | | */ |
905 | | |
906 | | GMutex * |
907 | | _g_async_queue_get_mutex (GAsyncQueue *queue) |
908 | 0 | { |
909 | 0 | g_return_val_if_fail (queue, NULL); |
910 | | |
911 | 0 | return &queue->mutex; |
912 | 0 | } |