1 /*****************************************************************************
2 * fifo.c: FIFO management functions
3 *****************************************************************************
4 * Copyright (C) 2003-2004 VLC authors and VideoLAN
5 * Copyright (C) 2007-2015 RĂ©mi Denis-Courmont
7 * Authors: Laurent Aimar <fenrir@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program 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
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_block.h>
36 * @section Thread-safe block queue functions
40 * Internal state for block queues
44 vlc_mutex_t lock; /* fifo data lock */
45 vlc_cond_t wait; /**< Wait for data */
46 vlc_cond_t wait_room; /**< Wait for queue depth to shrink */
56 * Locks a block FIFO. No more than one thread can lock the FIFO at any given
57 * time, and no other thread can modify the FIFO while it is locked.
58 * vlc_fifo_Unlock() releases the lock.
60 * @note If the FIFO is already locked by another thread, this function waits.
61 * This function is not a cancellation point.
63 * @warning Recursively locking a single FIFO is undefined. Locking more than
64 * one FIFO at a time may lead to lock inversion; mind the locking order.
66 void vlc_fifo_Lock(vlc_fifo_t *fifo)
68 vlc_mutex_lock(&fifo->lock);
72 * Unlocks a block FIFO previously locked by block_FifoLock().
74 * @note This function is not a cancellation point.
76 * @warning Unlocking a FIFO not locked by the calling thread is undefined.
78 void vlc_fifo_Unlock(vlc_fifo_t *fifo)
80 vlc_mutex_unlock(&fifo->lock);
84 * Wakes up one thread waiting on the FIFO, if any.
86 * @note This function is not a cancellation point.
88 * @warning For race-free operations, the FIFO should be locked by the calling
89 * thread. The function can be called on a unlocked FIFO however.
91 void vlc_fifo_Signal(vlc_fifo_t *fifo)
93 vlc_cond_signal(&fifo->wait);
97 * Atomically unlocks the FIFO and waits until one thread signals the FIFO,
98 * then locks the FIFO again. A signal can be sent by queueing a block to the
99 * previously empty FIFO or by calling vlc_fifo_Signal() directly.
100 * This function may also return spuriously at any moment.
102 * @note This function is a cancellation point. In case of cancellation, the
103 * the FIFO will be locked before cancellation cleanup handlers are processed.
105 void vlc_fifo_Wait(vlc_fifo_t *fifo)
107 vlc_fifo_WaitCond(fifo, &fifo->wait);
110 void vlc_fifo_WaitCond(vlc_fifo_t *fifo, vlc_cond_t *condvar)
112 vlc_cond_wait(condvar, &fifo->lock);
116 * Checks how many blocks are queued in a locked FIFO.
118 * @note This function is not cancellation point.
120 * @warning The FIFO must be locked by the calling thread using
121 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
123 * @return the number of blocks in the FIFO (zero if it is empty)
125 size_t vlc_fifo_GetCount(const vlc_fifo_t *fifo)
127 return fifo->i_depth;
131 * Checks how many bytes are queued in a locked FIFO.
133 * @note This function is not cancellation point.
135 * @warning The FIFO must be locked by the calling thread using
136 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
138 * @return the total number of bytes
140 * @note Zero bytes does not necessarily mean that the FIFO is empty since
141 * a block could contain zero bytes. Use vlc_fifo_GetCount() to determine if
144 size_t vlc_fifo_GetBytes(const vlc_fifo_t *fifo)
150 * Queues a linked-list of blocks into a locked FIFO.
152 * @param block the head of the list of blocks
153 * (if NULL, this function has no effects)
155 * @note This function is not a cancellation point.
157 * @warning The FIFO must be locked by the calling thread using
158 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
160 void vlc_fifo_QueueUnlocked(block_fifo_t *fifo, block_t *block)
162 vlc_assert_locked(&fifo->lock);
163 assert(*(fifo->pp_last) == NULL);
165 *(fifo->pp_last) = block;
167 while (block != NULL)
169 fifo->pp_last = &block->p_next;
171 fifo->i_size += block->i_size;
173 block = block->p_next;
176 vlc_cond_signal(&fifo->wait);
180 * Dequeues the first block from a locked FIFO, if any.
182 * @note This function is not a cancellation point.
184 * @warning The FIFO must be locked by the calling thread using
185 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
187 * @return the first block in the FIFO or NULL if the FIFO is empty
189 block_t *vlc_fifo_DequeueUnlocked(block_fifo_t *fifo)
191 vlc_assert_locked(&fifo->lock);
193 block_t *block = fifo->p_first;
196 return NULL; /* Nothing to do */
198 fifo->p_first = block->p_next;
199 if (block->p_next == NULL)
200 fifo->pp_last = &fifo->p_first;
201 block->p_next = NULL;
203 assert(fifo->i_depth > 0);
205 assert(fifo->i_size >= block->i_buffer);
206 fifo->i_size -= block->i_buffer;
208 /* We don't know how many threads can queue new packets now. */
209 vlc_cond_broadcast(&fifo->wait_room);
215 * Dequeues the all blocks from a locked FIFO. This is equivalent to calling
216 * vlc_fifo_DequeueUnlocked() repeatedly until the FIFO is emptied, but this
217 * function is faster.
219 * @note This function is not a cancellation point.
221 * @warning The FIFO must be locked by the calling thread using
222 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
224 * @return a linked-list of all blocks in the FIFO (possibly NULL)
226 block_t *vlc_fifo_DequeueAllUnlocked(block_fifo_t *fifo)
228 vlc_assert_locked(&fifo->lock);
230 block_t *block = fifo->p_first;
232 fifo->p_first = NULL;
233 fifo->pp_last = &fifo->p_first;
237 /* We don't know how many threads can queue new packets now. */
238 vlc_cond_broadcast(&fifo->wait_room);
245 * Creates a thread-safe FIFO queue of blocks.
246 * See also block_FifoPut() and block_FifoGet().
247 * @return the FIFO or NULL on memory error
249 block_fifo_t *block_FifoNew( void )
251 block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
255 vlc_mutex_init( &p_fifo->lock );
256 vlc_cond_init( &p_fifo->wait );
257 vlc_cond_init( &p_fifo->wait_room );
258 p_fifo->p_first = NULL;
259 p_fifo->pp_last = &p_fifo->p_first;
260 p_fifo->i_depth = p_fifo->i_size = 0;
261 p_fifo->b_force_wake = false;
267 * Destroys a FIFO created by block_FifoNew().
268 * Any queued blocks are also destroyed.
270 void block_FifoRelease( block_fifo_t *p_fifo )
272 block_ChainRelease( p_fifo->p_first );
273 vlc_cond_destroy( &p_fifo->wait_room );
274 vlc_cond_destroy( &p_fifo->wait );
275 vlc_mutex_destroy( &p_fifo->lock );
280 * Clears all blocks in a FIFO.
282 void block_FifoEmpty(block_fifo_t *fifo)
287 block = vlc_fifo_DequeueAllUnlocked(fifo);
288 vlc_fifo_Unlock(fifo);
289 block_ChainRelease(block);
293 * Wait until the FIFO gets below a certain size (if needed).
295 * Note that if more than one thread writes to the FIFO, you cannot assume that
296 * the FIFO is actually below the requested size upon return (since another
297 * thread could have refilled it already). This is typically not an issue, as
298 * this function is meant for (relaxed) congestion control.
300 * This function may be a cancellation point and it is cancel-safe.
302 * @param fifo queue to wait on
303 * @param max_depth wait until the queue has no more than this many blocks
304 * (use SIZE_MAX to ignore this constraint)
305 * @param max_size wait until the queue has no more than this many bytes
306 * (use SIZE_MAX to ignore this constraint)
309 void block_FifoPace (block_fifo_t *fifo, size_t max_depth, size_t max_size)
313 vlc_mutex_lock (&fifo->lock);
314 while ((fifo->i_depth > max_depth) || (fifo->i_size > max_size))
316 mutex_cleanup_push (&fifo->lock);
317 vlc_cond_wait (&fifo->wait_room, &fifo->lock);
320 vlc_mutex_unlock (&fifo->lock);
324 * Immediately queue one block at the end of a FIFO.
326 * @param block head of a block list to queue (may be NULL)
328 void block_FifoPut(block_fifo_t *fifo, block_t *block)
331 vlc_fifo_QueueUnlocked(fifo, block);
332 vlc_fifo_Unlock(fifo);
335 void block_FifoWake( block_fifo_t *p_fifo )
337 vlc_mutex_lock( &p_fifo->lock );
338 if( p_fifo->p_first == NULL )
339 p_fifo->b_force_wake = true;
340 vlc_cond_broadcast( &p_fifo->wait );
341 vlc_mutex_unlock( &p_fifo->lock );
345 * Dequeue the first block from the FIFO. If necessary, wait until there is
346 * one block in the queue. This function is (always) cancellation point.
348 * @return a valid block, or NULL if block_FifoWake() was called.
350 block_t *block_FifoGet(block_fifo_t *fifo)
357 while (vlc_fifo_IsEmpty(fifo) && !fifo->b_force_wake)
359 vlc_fifo_CleanupPush(fifo);
363 fifo->b_force_wake = false;
364 block = vlc_fifo_DequeueUnlocked(fifo);
365 vlc_fifo_Unlock(fifo);
371 * Peeks the first block in the FIFO.
372 * If necessary, wait until there is one block.
373 * This function is (always) a cancellation point.
375 * @warning This function leaves the block in the FIFO.
376 * You need to protect against concurrent threads who could dequeue the block.
377 * Preferrably, there should be only one thread reading from the FIFO.
379 * @return a valid block.
381 block_t *block_FifoShow( block_fifo_t *p_fifo )
387 vlc_mutex_lock( &p_fifo->lock );
388 mutex_cleanup_push( &p_fifo->lock );
390 while( p_fifo->p_first == NULL )
391 vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
399 /* FIXME: not (really) thread-safe */
400 size_t block_FifoSize (block_fifo_t *fifo)
404 vlc_mutex_lock (&fifo->lock);
406 vlc_mutex_unlock (&fifo->lock);
410 /* FIXME: not (really) thread-safe */
411 size_t block_FifoCount (block_fifo_t *fifo)
415 vlc_mutex_lock (&fifo->lock);
416 depth = fifo->i_depth;
417 vlc_mutex_unlock (&fifo->lock);