]> git.sesse.net Git - vlc/blob - src/misc/fifo.c
b93ab1151dbc5cc97742eb4cd647a5f2a9de204d
[vlc] / src / misc / fifo.c
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
6  *
7  * Authors: Laurent Aimar <fenrir@videolan.org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29 #include <stdlib.h>
30
31 #include <vlc_common.h>
32 #include <vlc_block.h>
33 #include "libvlc.h"
34
35 /**
36  * @section Thread-safe block queue functions
37  */
38
39 /**
40  * Internal state for block queues
41  */
42 struct block_fifo_t
43 {
44     vlc_mutex_t         lock;                         /* fifo data lock */
45     vlc_cond_t          wait;      /**< Wait for data */
46
47     block_t             *p_first;
48     block_t             **pp_last;
49     size_t              i_depth;
50     size_t              i_size;
51 };
52
53 /**
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.
57  *
58  * @note If the FIFO is already locked by another thread, this function waits.
59  * This function is not a cancellation point.
60  *
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.
63  */
64 void vlc_fifo_Lock(vlc_fifo_t *fifo)
65 {
66     vlc_mutex_lock(&fifo->lock);
67 }
68
69 /**
70  * Unlocks a block FIFO previously locked by block_FifoLock().
71  *
72  * @note This function is not a cancellation point.
73  *
74  * @warning Unlocking a FIFO not locked by the calling thread is undefined.
75  */
76 void vlc_fifo_Unlock(vlc_fifo_t *fifo)
77 {
78     vlc_mutex_unlock(&fifo->lock);
79 }
80
81 /**
82  * Wakes up one thread waiting on the FIFO, if any.
83  *
84  * @note This function is not a cancellation point.
85  *
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.
88  */
89 void vlc_fifo_Signal(vlc_fifo_t *fifo)
90 {
91     vlc_cond_signal(&fifo->wait);
92 }
93
94 /**
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.
99  *
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.
102  */
103 void vlc_fifo_Wait(vlc_fifo_t *fifo)
104 {
105     vlc_fifo_WaitCond(fifo, &fifo->wait);
106 }
107
108 void vlc_fifo_WaitCond(vlc_fifo_t *fifo, vlc_cond_t *condvar)
109 {
110     vlc_cond_wait(condvar, &fifo->lock);
111 }
112
113 /**
114  * Checks how many blocks are queued in a locked FIFO.
115  *
116  * @note This function is not cancellation point.
117  *
118  * @warning The FIFO must be locked by the calling thread using
119  * vlc_fifo_Lock(). Otherwise behaviour is undefined.
120  *
121  * @return the number of blocks in the FIFO (zero if it is empty)
122  */
123 size_t vlc_fifo_GetCount(const vlc_fifo_t *fifo)
124 {
125     return fifo->i_depth;
126 }
127
128 /**
129  * Checks how many bytes are queued in a locked FIFO.
130  *
131  * @note This function is not cancellation point.
132  *
133  * @warning The FIFO must be locked by the calling thread using
134  * vlc_fifo_Lock(). Otherwise behaviour is undefined.
135  *
136  * @return the total number of bytes
137  *
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
140  * a FIFO is empty.
141  */
142 size_t vlc_fifo_GetBytes(const vlc_fifo_t *fifo)
143 {
144     return fifo->i_size;
145 }
146
147 /**
148  * Queues a linked-list of blocks into a locked FIFO.
149  *
150  * @param block the head of the list of blocks
151  *              (if NULL, this function has no effects)
152  *
153  * @note This function is not a cancellation point.
154  *
155  * @warning The FIFO must be locked by the calling thread using
156  * vlc_fifo_Lock(). Otherwise behaviour is undefined.
157  */
158 void vlc_fifo_QueueUnlocked(block_fifo_t *fifo, block_t *block)
159 {
160     vlc_assert_locked(&fifo->lock);
161     assert(*(fifo->pp_last) == NULL);
162
163     *(fifo->pp_last) = block;
164
165     while (block != NULL)
166     {
167         fifo->pp_last = &block->p_next;
168         fifo->i_depth++;
169         fifo->i_size += block->i_size;
170
171         block = block->p_next;
172     }
173
174     vlc_cond_signal(&fifo->wait);
175 }
176
177 /**
178  * Dequeues the first block from a locked FIFO, if any.
179  *
180  * @note This function is not a cancellation point.
181  *
182  * @warning The FIFO must be locked by the calling thread using
183  * vlc_fifo_Lock(). Otherwise behaviour is undefined.
184  *
185  * @return the first block in the FIFO or NULL if the FIFO is empty
186  */
187 block_t *vlc_fifo_DequeueUnlocked(block_fifo_t *fifo)
188 {
189     vlc_assert_locked(&fifo->lock);
190
191     block_t *block = fifo->p_first;
192
193     if (block == NULL)
194         return NULL; /* Nothing to do */
195
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;
200
201     assert(fifo->i_depth > 0);
202     fifo->i_depth--;
203     assert(fifo->i_size >= block->i_buffer);
204     fifo->i_size -= block->i_buffer;
205
206     return block;
207 }
208
209 /**
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.
213  *
214  * @note This function is not a cancellation point.
215  *
216  * @warning The FIFO must be locked by the calling thread using
217  * vlc_fifo_Lock(). Otherwise behaviour is undefined.
218  *
219  * @return a linked-list of all blocks in the FIFO (possibly NULL)
220  */
221 block_t *vlc_fifo_DequeueAllUnlocked(block_fifo_t *fifo)
222 {
223     vlc_assert_locked(&fifo->lock);
224
225     block_t *block = fifo->p_first;
226
227     fifo->p_first = NULL;
228     fifo->pp_last = &fifo->p_first;
229     fifo->i_depth = 0;
230     fifo->i_size = 0;
231
232     return block;
233 }
234
235
236 /**
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
240  */
241 block_fifo_t *block_FifoNew( void )
242 {
243     block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
244     if( !p_fifo )
245         return NULL;
246
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;
252
253     return p_fifo;
254 }
255
256 /**
257  * Destroys a FIFO created by block_FifoNew().
258  * Any queued blocks are also destroyed.
259  */
260 void block_FifoRelease( block_fifo_t *p_fifo )
261 {
262     block_ChainRelease( p_fifo->p_first );
263     vlc_cond_destroy( &p_fifo->wait );
264     vlc_mutex_destroy( &p_fifo->lock );
265     free( p_fifo );
266 }
267
268 /**
269  * Clears all blocks in a FIFO.
270  */
271 void block_FifoEmpty(block_fifo_t *fifo)
272 {
273     block_t *block;
274
275     vlc_fifo_Lock(fifo);
276     block = vlc_fifo_DequeueAllUnlocked(fifo);
277     vlc_fifo_Unlock(fifo);
278     block_ChainRelease(block);
279 }
280
281 /**
282  * Immediately queue one block at the end of a FIFO.
283  * @param fifo queue
284  * @param block head of a block list to queue (may be NULL)
285  */
286 void block_FifoPut(block_fifo_t *fifo, block_t *block)
287 {
288     vlc_fifo_Lock(fifo);
289     vlc_fifo_QueueUnlocked(fifo, block);
290     vlc_fifo_Unlock(fifo);
291 }
292
293 /**
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.
296  *
297  * @return a valid block, or NULL if block_FifoWake() was called.
298  */
299 block_t *block_FifoGet(block_fifo_t *fifo)
300 {
301     block_t *block;
302
303     vlc_testcancel();
304
305     vlc_fifo_Lock(fifo);
306     while (vlc_fifo_IsEmpty(fifo))
307     {
308         vlc_fifo_CleanupPush(fifo);
309         vlc_fifo_Wait(fifo);
310         vlc_cleanup_pop();
311     }
312     block = vlc_fifo_DequeueUnlocked(fifo);
313     vlc_fifo_Unlock(fifo);
314
315     return block;
316 }
317
318 /**
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.
322  *
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.
326  *
327  * @return a valid block.
328  */
329 block_t *block_FifoShow( block_fifo_t *p_fifo )
330 {
331     block_t *b;
332
333     vlc_testcancel( );
334
335     vlc_mutex_lock( &p_fifo->lock );
336     mutex_cleanup_push( &p_fifo->lock );
337
338     while( p_fifo->p_first == NULL )
339         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
340
341     b = p_fifo->p_first;
342
343     vlc_cleanup_run ();
344     return b;
345 }
346
347 /* FIXME: not (really) thread-safe */
348 size_t block_FifoSize (block_fifo_t *fifo)
349 {
350     size_t size;
351
352     vlc_mutex_lock (&fifo->lock);
353     size = fifo->i_size;
354     vlc_mutex_unlock (&fifo->lock);
355     return size;
356 }
357
358 /* FIXME: not (really) thread-safe */
359 size_t block_FifoCount (block_fifo_t *fifo)
360 {
361     size_t depth;
362
363     vlc_mutex_lock (&fifo->lock);
364     depth = fifo->i_depth;
365     vlc_mutex_unlock (&fifo->lock);
366     return depth;
367 }