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 */
54 * Locks a block FIFO. No more than one thread can lock the FIFO at any given
55 * time, and no other thread can modify the FIFO while it is locked.
56 * vlc_fifo_Unlock() releases the lock.
58 * @note If the FIFO is already locked by another thread, this function waits.
59 * This function is not a cancellation point.
61 * @warning Recursively locking a single FIFO is undefined. Locking more than
62 * one FIFO at a time may lead to lock inversion; mind the locking order.
64 void vlc_fifo_Lock(vlc_fifo_t *fifo)
66 vlc_mutex_lock(&fifo->lock);
70 * Unlocks a block FIFO previously locked by block_FifoLock().
72 * @note This function is not a cancellation point.
74 * @warning Unlocking a FIFO not locked by the calling thread is undefined.
76 void vlc_fifo_Unlock(vlc_fifo_t *fifo)
78 vlc_mutex_unlock(&fifo->lock);
82 * Wakes up one thread waiting on the FIFO, if any.
84 * @note This function is not a cancellation point.
86 * @warning For race-free operations, the FIFO should be locked by the calling
87 * thread. The function can be called on a unlocked FIFO however.
89 void vlc_fifo_Signal(vlc_fifo_t *fifo)
91 vlc_cond_signal(&fifo->wait);
95 * Atomically unlocks the FIFO and waits until one thread signals the FIFO,
96 * then locks the FIFO again. A signal can be sent by queueing a block to the
97 * previously empty FIFO or by calling vlc_fifo_Signal() directly.
98 * This function may also return spuriously at any moment.
100 * @note This function is a cancellation point. In case of cancellation, the
101 * the FIFO will be locked before cancellation cleanup handlers are processed.
103 void vlc_fifo_Wait(vlc_fifo_t *fifo)
105 vlc_fifo_WaitCond(fifo, &fifo->wait);
108 void vlc_fifo_WaitCond(vlc_fifo_t *fifo, vlc_cond_t *condvar)
110 vlc_cond_wait(condvar, &fifo->lock);
114 * Checks how many blocks are queued in a locked FIFO.
116 * @note This function is not cancellation point.
118 * @warning The FIFO must be locked by the calling thread using
119 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
121 * @return the number of blocks in the FIFO (zero if it is empty)
123 size_t vlc_fifo_GetCount(const vlc_fifo_t *fifo)
125 return fifo->i_depth;
129 * Checks how many bytes are queued in a locked FIFO.
131 * @note This function is not cancellation point.
133 * @warning The FIFO must be locked by the calling thread using
134 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
136 * @return the total number of bytes
138 * @note Zero bytes does not necessarily mean that the FIFO is empty since
139 * a block could contain zero bytes. Use vlc_fifo_GetCount() to determine if
142 size_t vlc_fifo_GetBytes(const vlc_fifo_t *fifo)
148 * Queues a linked-list of blocks into a locked FIFO.
150 * @param block the head of the list of blocks
151 * (if NULL, this function has no effects)
153 * @note This function is not a cancellation point.
155 * @warning The FIFO must be locked by the calling thread using
156 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
158 void vlc_fifo_QueueUnlocked(block_fifo_t *fifo, block_t *block)
160 vlc_assert_locked(&fifo->lock);
161 assert(*(fifo->pp_last) == NULL);
163 *(fifo->pp_last) = block;
165 while (block != NULL)
167 fifo->pp_last = &block->p_next;
169 fifo->i_size += block->i_size;
171 block = block->p_next;
174 vlc_cond_signal(&fifo->wait);
178 * Dequeues the first block from a locked FIFO, if any.
180 * @note This function is not a cancellation point.
182 * @warning The FIFO must be locked by the calling thread using
183 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
185 * @return the first block in the FIFO or NULL if the FIFO is empty
187 block_t *vlc_fifo_DequeueUnlocked(block_fifo_t *fifo)
189 vlc_assert_locked(&fifo->lock);
191 block_t *block = fifo->p_first;
194 return NULL; /* Nothing to do */
196 fifo->p_first = block->p_next;
197 if (block->p_next == NULL)
198 fifo->pp_last = &fifo->p_first;
199 block->p_next = NULL;
201 assert(fifo->i_depth > 0);
203 assert(fifo->i_size >= block->i_buffer);
204 fifo->i_size -= block->i_buffer;
210 * Dequeues the all blocks from a locked FIFO. This is equivalent to calling
211 * vlc_fifo_DequeueUnlocked() repeatedly until the FIFO is emptied, but this
212 * function is faster.
214 * @note This function is not a cancellation point.
216 * @warning The FIFO must be locked by the calling thread using
217 * vlc_fifo_Lock(). Otherwise behaviour is undefined.
219 * @return a linked-list of all blocks in the FIFO (possibly NULL)
221 block_t *vlc_fifo_DequeueAllUnlocked(block_fifo_t *fifo)
223 vlc_assert_locked(&fifo->lock);
225 block_t *block = fifo->p_first;
227 fifo->p_first = NULL;
228 fifo->pp_last = &fifo->p_first;
237 * Creates a thread-safe FIFO queue of blocks.
238 * See also block_FifoPut() and block_FifoGet().
239 * @return the FIFO or NULL on memory error
241 block_fifo_t *block_FifoNew( void )
243 block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
247 vlc_mutex_init( &p_fifo->lock );
248 vlc_cond_init( &p_fifo->wait );
249 p_fifo->p_first = NULL;
250 p_fifo->pp_last = &p_fifo->p_first;
251 p_fifo->i_depth = p_fifo->i_size = 0;
257 * Destroys a FIFO created by block_FifoNew().
258 * Any queued blocks are also destroyed.
260 void block_FifoRelease( block_fifo_t *p_fifo )
262 block_ChainRelease( p_fifo->p_first );
263 vlc_cond_destroy( &p_fifo->wait );
264 vlc_mutex_destroy( &p_fifo->lock );
269 * Clears all blocks in a FIFO.
271 void block_FifoEmpty(block_fifo_t *fifo)
276 block = vlc_fifo_DequeueAllUnlocked(fifo);
277 vlc_fifo_Unlock(fifo);
278 block_ChainRelease(block);
282 * Immediately queue one block at the end of a FIFO.
284 * @param block head of a block list to queue (may be NULL)
286 void block_FifoPut(block_fifo_t *fifo, block_t *block)
289 vlc_fifo_QueueUnlocked(fifo, block);
290 vlc_fifo_Unlock(fifo);
294 * Dequeue the first block from the FIFO. If necessary, wait until there is
295 * one block in the queue. This function is (always) cancellation point.
297 * @return a valid block, or NULL if block_FifoWake() was called.
299 block_t *block_FifoGet(block_fifo_t *fifo)
306 while (vlc_fifo_IsEmpty(fifo))
308 vlc_fifo_CleanupPush(fifo);
312 block = vlc_fifo_DequeueUnlocked(fifo);
313 vlc_fifo_Unlock(fifo);
319 * Peeks the first block in the FIFO.
320 * If necessary, wait until there is one block.
321 * This function is (always) a cancellation point.
323 * @warning This function leaves the block in the FIFO.
324 * You need to protect against concurrent threads who could dequeue the block.
325 * Preferrably, there should be only one thread reading from the FIFO.
327 * @return a valid block.
329 block_t *block_FifoShow( block_fifo_t *p_fifo )
335 vlc_mutex_lock( &p_fifo->lock );
336 mutex_cleanup_push( &p_fifo->lock );
338 while( p_fifo->p_first == NULL )
339 vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
347 /* FIXME: not (really) thread-safe */
348 size_t block_FifoSize (block_fifo_t *fifo)
352 vlc_mutex_lock (&fifo->lock);
354 vlc_mutex_unlock (&fifo->lock);
358 /* FIXME: not (really) thread-safe */
359 size_t block_FifoCount (block_fifo_t *fifo)
363 vlc_mutex_lock (&fifo->lock);
364 depth = fifo->i_depth;
365 vlc_mutex_unlock (&fifo->lock);