|
|
|
|
/*****************************************************************************
|
|
|
|
|
* vlc_queue.h: generic queue functions
|
|
|
|
|
*****************************************************************************
|
|
|
|
|
* Copyright (C) 2020 Rémi Denis-Courmont
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifndef VLC_QUEUE_H
|
|
|
|
|
#define VLC_QUEUE_H
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @defgroup queue Thread-safe queues (FIFO)
|
|
|
|
|
* @ingroup cext
|
|
|
|
|
* @{
|
|
|
|
|
* @file vlc_queue.h
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <vlc_common.h>
|
|
|
|
|
#include <vlc_threads.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Opaque type for queue entry.
|
|
|
|
|
*/
|
|
|
|
|
struct vlc_queue_entry;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Thread-safe queue (a.k.a. FIFO).
|
|
|
|
|
*/
|
|
|
|
|
typedef struct vlc_queue
|
|
|
|
|
{
|
|
|
|
|
struct vlc_queue_entry *first;
|
|
|
|
|
struct vlc_queue_entry **lastp;
|
|
|
|
|
ptrdiff_t next_offset;
|
|
|
|
|
vlc_mutex_t lock;
|
|
|
|
|
vlc_cond_t wait;
|
|
|
|
|
} vlc_queue_t;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initializes a queue.
|
|
|
|
|
*
|
|
|
|
|
* @param queue storage space for the queue
|
|
|
|
|
* @param next_offset offset of the pointer to the next element
|
|
|
|
|
* within a queue entry (as per @c offsetof())
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_queue_Init(vlc_queue_t *queue, ptrdiff_t next_offset);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @defgroup queue_ll Queue internals
|
|
|
|
|
*
|
|
|
|
|
* Low-level queue functions.
|
|
|
|
|
*
|
|
|
|
|
* In some cases, the high-level queue functions do not exactly fit the
|
|
|
|
|
* use case requirements, and it is necessary to access the queue internals.
|
|
|
|
|
* This typically occurs when threads wait for elements to be added to the
|
|
|
|
|
* queue at the same time as some other type of events.
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
* Locks a queue.
|
|
|
|
|
*
|
|
|
|
|
* No more than one thread can lock a queue at any given time, and no other
|
|
|
|
|
* thread can modify the queue while it is locked.
|
|
|
|
|
* Accordingly, if the queue is already locked by another thread, this function
|
|
|
|
|
* waits.
|
|
|
|
|
*
|
|
|
|
|
* Use vlc_queue_Unlock() to release the lock.
|
|
|
|
|
*
|
|
|
|
|
* @warning Recursively locking a single queue is undefined.
|
|
|
|
|
* Also locking more than one queue at a time may lead to lock inversion:
|
|
|
|
|
* mind the locking order!
|
|
|
|
|
*/
|
|
|
|
|
static inline void vlc_queue_Lock(vlc_queue_t *q)
|
|
|
|
|
{
|
|
|
|
|
vlc_mutex_lock(&q->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unlocks a queue.
|
|
|
|
|
*
|
|
|
|
|
* This releases the lock on a queue, allowing other threads to manipulate the
|
|
|
|
|
* queue. The behaviour is undefined if the calling thread is not holding the
|
|
|
|
|
* queue lock.
|
|
|
|
|
*/
|
|
|
|
|
static inline void vlc_queue_Unlock(vlc_queue_t *q)
|
|
|
|
|
{
|
|
|
|
|
vlc_mutex_unlock(&q->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wakes one thread waiting for a queue entry up.
|
|
|
|
|
*/
|
|
|
|
|
static inline void vlc_queue_Signal(vlc_queue_t *q)
|
|
|
|
|
{
|
|
|
|
|
vlc_cond_signal(&q->wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Waits for a queue entry.
|
|
|
|
|
*
|
|
|
|
|
* @note This function is a cancellation point.
|
|
|
|
|
* In case of cancellation, the queue will be locked,
|
|
|
|
|
* as is consistent for condition variable semantics.
|
|
|
|
|
*
|
|
|
|
|
* @bug This function should probably not be aware of cancellation.
|
|
|
|
|
*/
|
|
|
|
|
static inline void vlc_queue_Wait(vlc_queue_t *q)
|
|
|
|
|
{
|
|
|
|
|
vlc_cond_wait(&q->wait, &q->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Queues an entry (without locking).
|
|
|
|
|
*
|
|
|
|
|
* This function enqueues an entry, or rather a linked-list of entries, in a
|
|
|
|
|
* thread-safe queue, without taking the queue lock.
|
|
|
|
|
*
|
|
|
|
|
* @warning It is assumed that the caller already holds the queue lock;
|
|
|
|
|
* otherwise the behaviour is undefined.
|
|
|
|
|
*
|
|
|
|
|
* @param entry NULL-terminated list of entries to queue
|
|
|
|
|
* (if NULL, this function has no effects)
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_queue_EnqueueUnlocked(vlc_queue_t *, void *entry);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dequeues the oldest entry (without locking).
|
|
|
|
|
*
|
|
|
|
|
* This function dequeues an entry from a thread-safe queue. It is assumed
|
|
|
|
|
* that the caller already holds the queue lock; otherwise the behaviour is
|
|
|
|
|
* undefined.
|
|
|
|
|
*
|
|
|
|
|
* @warning It is assumed that the caller already holds the queue lock;
|
|
|
|
|
* otherwise the behaviour is undefined.
|
|
|
|
|
*
|
|
|
|
|
* @return the first entry in the queue, or NULL if the queue is empty
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void *vlc_queue_DequeueUnlocked(vlc_queue_t *) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dequeues all entries (without locking).
|
|
|
|
|
*
|
|
|
|
|
* This is equivalent to calling vlc_queue_DequeueUnlocked() repeatedly until
|
|
|
|
|
* the queue is emptied. However this function is much faster than that, as it
|
|
|
|
|
* does not need to update the linked-list pointers.
|
|
|
|
|
*
|
|
|
|
|
* @warning It is assumed that the caller already holds the queue lock;
|
|
|
|
|
* otherwise the behaviour is undefined.
|
|
|
|
|
*
|
|
|
|
|
* @return a linked-list of all entries (possibly NULL if none)
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void *vlc_queue_DequeueAllUnlocked(vlc_queue_t *) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if a queue is empty (without locking).
|
|
|
|
|
*
|
|
|
|
|
* @warning It is assumed that the caller already holds the queue lock;
|
|
|
|
|
* otherwise the behaviour is undefined.
|
|
|
|
|
*
|
|
|
|
|
* @retval false the queue contains one or more entries
|
|
|
|
|
* @retval true the queue is empty
|
|
|
|
|
*/
|
|
|
|
|
VLC_USED static inline bool vlc_queue_IsEmpty(const vlc_queue_t *q)
|
|
|
|
|
{
|
|
|
|
|
return q->first == NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Queues an entry.
|
|
|
|
|
*
|
|
|
|
|
* This function enqueues an entry, or rather a linked-list of entries, in a
|
|
|
|
|
* thread-safe queue.
|
|
|
|
|
*
|
|
|
|
|
* @param entry list of entries (if NULL, this function has no effects)
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_queue_Enqueue(vlc_queue_t *, void *entry);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dequeues the oldest entry.
|
|
|
|
|
*
|
|
|
|
|
* This function dequeues an entry from a thread-safe queue. If the queue is
|
|
|
|
|
* empty, it will wait until at least one entry is available.
|
|
|
|
|
*
|
|
|
|
|
* @param queue queue object to dequeue an entry from
|
|
|
|
|
*
|
|
|
|
|
* @return the first entry in the queue, or NULL if the queue is empty
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void *vlc_queue_Dequeue(vlc_queue_t *queue) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dequeues all entries.
|
|
|
|
|
*
|
|
|
|
|
* This is equivalent to calling vlc_queue_Dequeue() repeatedly until the queue
|
|
|
|
|
* is emptied. However this function is much faster than that, as it
|
|
|
|
|
* does not need to update the linked-list pointers.
|
|
|
|
|
*
|
|
|
|
|
* @return a linked-list of all entries (possibly NULL if none)
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void *vlc_queue_DequeueAll(vlc_queue_t *) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @defgroup queue_killable Killable queues
|
|
|
|
|
*
|
|
|
|
|
* Thread-safe queues with an end flag.
|
|
|
|
|
*
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Marks a queue ended.
|
|
|
|
|
*/
|
|
|
|
|
static inline void vlc_queue_Kill(vlc_queue_t *q,
|
|
|
|
|
bool *restrict tombstone)
|
|
|
|
|
{
|
|
|
|
|
vlc_queue_Lock(q);
|
|
|
|
|
*tombstone = true;
|
|
|
|
|
vlc_queue_Signal(q);
|
|
|
|
|
vlc_queue_Unlock(q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dequeues one entry from a killable queue.
|
|
|
|
|
*
|
|
|
|
|
* @return an entry, or NULL if the queue is empty and has been ended.
|
|
|
|
|
*/
|
|
|
|
|
static inline void *vlc_queue_DequeueKillable(vlc_queue_t *q,
|
vlc_queue: fix restrict violation
`restrict` tells the compiler that the tombstone pointer is the only way
to access the variable pointed by it. This is not case with multi-threading.
From the C11 specs:
§ 6.7.3 item 8
An object that is accessed through a restrict-qualified pointer has
a special association with that pointer. This association, defined
in 6.7.3.1 below, requires that all accesses to that object use,
directly or indirectly, the value of that particular pointer.
§ 6.7.3.1 item 4
Every other lvalue used to access the value of X shall also have its
address based on P.
Also set the pointer to const.
Fixes the following race condition when closing live555:
Thread 80 (Thread 28393.4880):
0 0x0000007953dbebbc in syscall () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
1 0x0000007855d2c0ec in sys_futex (addr=0x89, op=137, val=0, to=0x0, addr2=0x0, val3=-1) at ../../src/linux/thread.c:82
2 vlc_futex_wait (addr=0x89, flags=0, val=0, to=0x0) at ../../src/linux/thread.c:93
3 vlc_atomic_wait (addr=0x89, val=0) at ../../src/linux/thread.c:109
4 0x0000007855d20e9c in vlc_cond_wait (cond=cond@entry=0x7829f26ce8, mutex=mutex@entry=0x7829f26cd8) at ../../src/misc/threads.c:298
5 0x0000007855d42244 in vlc_queue_Wait (q=0x7829f26cc0) at ../../include/vlc_queue.h:122
6 vlc_queue_DequeueKillable (q=0x7829f26cc0, tombstone=0x7829f26d00) at ../../include/vlc_queue.h:248
7 vlc_stream_fifo_Block (s=<optimized out>, eof=0x78709a8838) at ../../src/input/stream_fifo.c:75
8 0x0000007855cfa160 in vlc_stream_ReadRaw (s=s@entry=0x7819411360, buf=buf@entry=0x78c4393c80, len=len@entry=188) at ../../src/input/stream.c:466
9 0x0000007855cf9fe0 in vlc_stream_ReadPartial (s=s@entry=0x7819411360, buf=buf@entry=0x78c4393c80, len=len@entry=188) at ../../src/input/stream.c:489
10 0x0000007855cfad44 in vlc_stream_Read (s=<optimized out>, buf=0x78c4393c80, len=188) at ../../src/input/stream.c:504
11 vlc_stream_Block (s=0x7819411360, size=<optimized out>) at ../../src/input/stream.c:909
12 0x0000007855f1fe5c in ReadTSPacket (p_demux=p_demux@entry=0x78c42c0ce0) at ../../modules/demux/mpeg/ts.c:1808
13 0x0000007855f1de30 in Demux (p_demux=<optimized out>) at ../../modules/demux/mpeg/ts.c:649
4 0x0000007855d41e4c in vlc_demux_chained_Thread (data=0x7829f26c70) at ../../src/input/demux_chained.c:96
15 0x0000007855d2d5b8 in joinable_thread (data=0x7823341ea0) at ../../src/android/thread.c:96
16 0x0000007953e217c4 in __pthread_start(void*) () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
17 0x0000007953dc41ac in __start_thread () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
Thread 79 (Thread 28393.4875):
0 0x0000007953dbebbc in syscall () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
1 0x0000007953e21d98 in pthread_join () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
2 0x0000007855d2d204 in vlc_join (handle=0x7823341ea0, result=0x0) at ../../src/android/thread.c:147
3 0x0000007855d4202c in vlc_demux_chained_Delete (dc=0x7829f26c70) at ../../src/input/demux_chained.c:167
4 0x0000007855d3d92c in Close (p_this=<optimized out>) at ../../modules/access/live555.cpp:505
5 0x0000007855cb49dc in module_unneed (obj=obj@entry=0x78194110c0, module=0x78c83b0520) at ../../src/modules/modules.c:305
6 0x0000007855ceae78 in vlc_access_Destroy (access=0x78194110c0) at ../../src/input/access.c:54
7 0x0000007855cf958c in vlc_stream_Delete (s=0x78194110c0) at ../../src/input/stream.c:150
8 0x0000007855ce4954 in demux_Delete (demux=0x78709a8d60) at ../../include/vlc_demux.h:291
9 InputSourceDestroy (in=0x781a3aa780) at ../../src/input/input.c:2852
10 0x0000007855ce39f0 in End (p_input=p_input@entry=0x7863073c00) at ../../src/input/input.c:1413
11 0x0000007855ce0620 in Run (data=0x7863073c00) at ../../src/input/input.c:431
12 0x0000007855d2d5b8 in joinable_thread (data=0x78231b02c0) at ../../src/android/thread.c:96
13 0x0000007953e217c4 in __pthread_start(void*) () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
14 0x0000007953dc41ac in __start_thread () from /home/tom/work/git/libvlcjni/libvlc/.gdb/obj/local/arm64-v8a/system/lib64/libc.so
3 years ago
|
|
|
const bool *tombstone)
|
|
|
|
|
{
|
|
|
|
|
void *entry;
|
|
|
|
|
|
|
|
|
|
vlc_queue_Lock(q);
|
|
|
|
|
while (vlc_queue_IsEmpty(q) && !*tombstone)
|
|
|
|
|
vlc_queue_Wait(q);
|
|
|
|
|
|
|
|
|
|
entry = vlc_queue_DequeueUnlocked(q);
|
|
|
|
|
vlc_queue_Unlock(q);
|
|
|
|
|
return entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
|
|
/** @} */
|
|
|
|
|
#endif
|