]> git.sesse.net Git - vlc/blob - src/misc/block.c
Replace block_New() with block_Alloc()
[vlc] / src / misc / block.c
1 /*****************************************************************************
2  * block.c: Data blocks management functions
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VLC authors and VideoLAN
5  * Copyright (C) 2007-2009 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 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <sys/stat.h>
32 #include <assert.h>
33 #include <errno.h>
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <fcntl.h>
38
39 #include <vlc_common.h>
40 #include <vlc_block.h>
41 #include <vlc_fs.h>
42
43 /**
44  * @section Block handling functions.
45  */
46
47 #ifndef NDEBUG
48 static void BlockNoRelease( block_t *b )
49 {
50     fprintf( stderr, "block %p has no release callback! This is a bug!\n", b );
51     abort();
52 }
53
54 static void block_Check (block_t *block)
55 {
56     while (block != NULL)
57     {
58         unsigned char *start = block->p_start;
59         unsigned char *end = block->p_start + block->i_size;
60         unsigned char *bufstart = block->p_buffer;
61         unsigned char *bufend = block->p_buffer + block->i_buffer;
62
63         assert (block->pf_release != BlockNoRelease);
64         assert (start <= end);
65         assert (bufstart <= bufend);
66         assert (bufstart >= start);
67         assert (bufend <= end);
68
69         block = block->p_next;
70     }
71 }
72
73 static void block_Invalidate (block_t *block)
74 {
75     block->p_next = NULL;
76     block_Check (block);
77     block->pf_release = BlockNoRelease;
78 }
79 #else
80 # define block_Check(b) ((void)(b))
81 # define block_Invalidate(b) ((void)(b))
82 #endif
83
84 void block_Init( block_t *restrict b, void *buf, size_t size )
85 {
86     /* Fill all fields to their default */
87     b->p_next = NULL;
88     b->p_buffer = buf;
89     b->i_buffer = size;
90     b->p_start = buf;
91     b->i_size = size;
92     b->i_flags = 0;
93     b->i_nb_samples = 0;
94     b->i_pts =
95     b->i_dts = VLC_TS_INVALID;
96     b->i_length = 0;
97 #ifndef NDEBUG
98     b->pf_release = BlockNoRelease;
99 #endif
100 }
101
102 static void block_generic_Release (block_t *block)
103 {
104     /* That is always true for blocks allocated with block_Alloc(). */
105     assert (block->p_start == (unsigned char *)(block + 1));
106     block_Invalidate (block);
107     free (block);
108 }
109
110 static void BlockMetaCopy( block_t *restrict out, const block_t *in )
111 {
112     out->p_next    = in->p_next;
113     out->i_nb_samples = in->i_nb_samples;
114     out->i_dts     = in->i_dts;
115     out->i_pts     = in->i_pts;
116     out->i_flags   = in->i_flags;
117     out->i_length  = in->i_length;
118 }
119
120 /* Memory alignment (must be a multiple of sizeof(void*) and a power of two) */
121 #define BLOCK_ALIGN        16
122 /* Initial reserved header and footer size (must be multiple of alignment) */
123 #define BLOCK_PADDING      32
124 /* Maximum size of reserved footer before we release with realloc() */
125 #define BLOCK_WASTE_SIZE   2048
126
127 block_t *block_Alloc (size_t size)
128 {
129     /* 2 * BLOCK_PADDING: pre + post padding */
130     const size_t alloc = sizeof (block_t) + BLOCK_ALIGN + (2 * BLOCK_PADDING)
131                        + size;
132     if (unlikely(alloc <= size))
133         return NULL;
134
135     block_t *b = malloc (alloc);
136     if (unlikely(b == NULL))
137         return NULL;
138
139     block_Init (b, b + 1, alloc - sizeof (*b));
140     static_assert ((BLOCK_PADDING % BLOCK_ALIGN) == 0,
141                    "BLOCK_PADDING must be a multiple of BLOCK_ALIGN");
142     b->p_buffer += BLOCK_PADDING + BLOCK_ALIGN - 1;
143     b->p_buffer = (void *)(((uintptr_t)b->p_buffer) & ~(BLOCK_ALIGN - 1));
144     b->i_buffer = size;
145     b->pf_release = block_generic_Release;
146     return b;
147 }
148
149 block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body )
150 {
151     size_t requested = i_prebody + i_body;
152
153     block_Check( p_block );
154
155     /* Corner case: empty block requested */
156     if( i_prebody <= 0 && i_body <= (size_t)(-i_prebody) )
157     {
158         block_Release( p_block );
159         return NULL;
160     }
161
162     assert( p_block->p_start <= p_block->p_buffer );
163     assert( p_block->p_start + p_block->i_size
164                                     >= p_block->p_buffer + p_block->i_buffer );
165
166     /* Corner case: the current payload is discarded completely */
167     if( i_prebody <= 0 && p_block->i_buffer <= (size_t)-i_prebody )
168          p_block->i_buffer = 0; /* discard current payload */
169     if( p_block->i_buffer == 0 )
170     {
171         if( requested <= p_block->i_size )
172         {   /* Enough room: recycle buffer */
173             size_t extra = p_block->i_size - requested;
174
175             p_block->p_buffer = p_block->p_start + (extra / 2);
176             p_block->i_buffer = requested;
177             return p_block;
178         }
179         /* Not enough room: allocate a new buffer */
180         block_t *p_rea = block_Alloc( requested );
181         if( p_rea )
182             BlockMetaCopy( p_rea, p_block );
183         block_Release( p_block );
184         return p_rea;
185     }
186
187     /* First, shrink payload */
188
189     /* Pull payload start */
190     if( i_prebody < 0 )
191     {
192         assert( p_block->i_buffer >= (size_t)-i_prebody );
193         p_block->p_buffer -= i_prebody;
194         p_block->i_buffer += i_prebody;
195         i_body += i_prebody;
196         i_prebody = 0;
197     }
198
199     /* Trim payload end */
200     if( p_block->i_buffer > i_body )
201         p_block->i_buffer = i_body;
202
203     uint8_t *p_start = p_block->p_start;
204     uint8_t *p_end = p_start + p_block->i_size;
205
206     /* Second, reallocate the buffer if we lack space. This is done now to
207      * minimize the payload size for memory copy. */
208     assert( i_prebody >= 0 );
209     if( (size_t)(p_block->p_buffer - p_start) < (size_t)i_prebody
210      || (size_t)(p_end - p_block->p_buffer) < i_body )
211     {
212         block_t *p_rea = block_Alloc( requested );
213         if( p_rea )
214         {
215             BlockMetaCopy( p_rea, p_block );
216             p_rea->p_buffer += i_prebody;
217             p_rea->i_buffer -= i_prebody;
218             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
219         }
220         block_Release( p_block );
221         if( p_rea == NULL )
222             return NULL;
223         p_block = p_rea;
224     }
225     else
226     /* We have a very large reserved footer now? Release some of it.
227      * XXX it might not preserve the alignment of p_buffer */
228     if( p_end - (p_block->p_buffer + i_body) > BLOCK_WASTE_SIZE )
229     {
230         block_t *p_rea = block_Alloc( requested );
231         if( p_rea )
232         {
233             BlockMetaCopy( p_rea, p_block );
234             p_rea->p_buffer += i_prebody;
235             p_rea->i_buffer -= i_prebody;
236             memcpy( p_rea->p_buffer, p_block->p_buffer, p_block->i_buffer );
237             block_Release( p_block );
238             p_block = p_rea;
239         }
240     }
241
242     /* NOTE: p_start and p_end are corrupted from this point */
243
244     /* Third, expand payload */
245
246     /* Push payload start */
247     if( i_prebody > 0 )
248     {
249         p_block->p_buffer -= i_prebody;
250         p_block->i_buffer += i_prebody;
251         i_body += i_prebody;
252         i_prebody = 0;
253     }
254
255     /* Expand payload to requested size */
256     p_block->i_buffer = i_body;
257
258     return p_block;
259 }
260
261
262 static void block_heap_Release (block_t *block)
263 {
264     block_Invalidate (block);
265     free (block->p_start);
266     free (block);
267 }
268
269 /**
270  * Creates a block from a heap allocation.
271  * This is provided by LibVLC so that manually heap-allocated blocks can safely
272  * be deallocated even after the origin plugin has been unloaded from memory.
273  *
274  * When block_Release() is called, VLC will free() the specified pointer.
275  *
276  * @param ptr base address of the heap allocation (will be free()'d)
277  * @param length bytes length of the heap allocation
278  * @return NULL in case of error (ptr free()'d in that case), or a valid
279  * block_t pointer.
280  */
281 block_t *block_heap_Alloc (void *addr, size_t length)
282 {
283     block_t *block = malloc (sizeof (*block));
284     if (block == NULL)
285     {
286         free (addr);
287         return NULL;
288     }
289
290     block_Init (block, addr, length);
291     block->pf_release = block_heap_Release;
292     return block;
293 }
294
295 #ifdef HAVE_MMAP
296 # include <sys/mman.h>
297
298 static void block_mmap_Release (block_t *block)
299 {
300     block_Invalidate (block);
301     munmap (block->p_start, block->i_size);
302     free (block);
303 }
304
305 /**
306  * Creates a block from a virtual address memory mapping (mmap).
307  * This is provided by LibVLC so that mmap blocks can safely be deallocated
308  * even after the allocating plugin has been unloaded from memory.
309  *
310  * @param addr base address of the mapping (as returned by mmap)
311  * @param length length (bytes) of the mapping (as passed to mmap)
312  * @return NULL if addr is MAP_FAILED, or an error occurred (in the later
313  * case, munmap(addr, length) is invoked before returning).
314  */
315 block_t *block_mmap_Alloc (void *addr, size_t length)
316 {
317     if (addr == MAP_FAILED)
318         return NULL;
319
320     block_t *block = malloc (sizeof (*block));
321     if (block == NULL)
322     {
323         munmap (addr, length);
324         return NULL;
325     }
326
327     block_Init (block, addr, length);
328     block->pf_release = block_mmap_Release;
329     return block;
330 }
331 #else
332 block_t *block_mmap_Alloc (void *addr, size_t length)
333 {
334     (void)addr; (void)length; return NULL;
335 }
336 #endif
337
338
339 #ifdef WIN32
340 # include <io.h>
341
342 static
343 ssize_t pread (int fd, void *buf, size_t count, off_t offset)
344 {
345     HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd);
346     if (handle == INVALID_HANDLE_VALUE)
347         return -1;
348
349     OVERLAPPED olap; olap.Offset = offset; olap.OffsetHigh = (offset >> 32);
350     DWORD written;
351     /* This braindead API will override the file pointer even if we specify
352      * an explicit read offset... So do not expect this to mix well with
353      * regular read() calls. */
354     if (ReadFile (handle, buf, count, &written, &olap))
355         return written;
356     return -1;
357 }
358 #endif
359
360 /**
361  * Loads a file into a block of memory through a file descriptor.
362  * If possible a private file mapping is created. Otherwise, the file is read
363  * normally. This function is a cancellation point.
364  *
365  * @note On 32-bits platforms,
366  * this function will not work for very large files,
367  * due to memory space constraints.
368  *
369  * @param fd file descriptor to load from
370  * @return a new block with the file content at p_buffer, and file length at
371  * i_buffer (release it with block_Release()), or NULL upon error (see errno).
372  */
373 block_t *block_File (int fd)
374 {
375     size_t length;
376     struct stat st;
377
378     /* First, get the file size */
379     if (fstat (fd, &st))
380         return NULL;
381
382     /* st_size is meaningful for regular files, shared memory and typed memory.
383      * It's also meaning for symlinks, but that's not possible with fstat().
384      * In other cases, it's undefined, and we should really not go further. */
385 #ifndef S_TYPEISSHM
386 # define S_TYPEISSHM( buf ) (0)
387 #endif
388     if (S_ISDIR (st.st_mode))
389     {
390         errno = EISDIR;
391         return NULL;
392     }
393     if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st))
394     {
395         errno = ESPIPE;
396         return NULL;
397     }
398
399     /* Prevent an integer overflow in mmap() and malloc() */
400     if (st.st_size >= SIZE_MAX)
401     {
402         errno = ENOMEM;
403         return NULL;
404     }
405     length = (size_t)st.st_size;
406
407 #ifdef HAVE_MMAP
408     if (length > 0)
409     {
410         void *addr;
411
412         addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
413         if (addr != MAP_FAILED)
414             return block_mmap_Alloc (addr, length);
415     }
416 #endif
417
418     /* If mmap() is not implemented by the OS _or_ the filesystem... */
419     block_t *block = block_Alloc (length);
420     if (block == NULL)
421         return NULL;
422     block_cleanup_push (block);
423
424     for (size_t i = 0; i < length;)
425     {
426         ssize_t len = pread (fd, block->p_buffer + i, length - i, i);
427         if (len == -1)
428         {
429             block_Release (block);
430             block = NULL;
431             break;
432         }
433         i += len;
434     }
435     vlc_cleanup_pop ();
436     return block;
437 }
438
439 /**
440  * Loads a file into a block of memory from the file path.
441  * See also block_File().
442  */
443 block_t *block_FilePath (const char *path)
444 {
445     int fd = vlc_open (path, O_RDONLY);
446     if (fd == -1)
447         return NULL;
448
449     block_t *block = block_File (fd);
450     close (fd);
451     return block;
452 }
453
454 /**
455  * @section Thread-safe block queue functions
456  */
457
458 /**
459  * Internal state for block queues
460  */
461 struct block_fifo_t
462 {
463     vlc_mutex_t         lock;                         /* fifo data lock */
464     vlc_cond_t          wait;      /**< Wait for data */
465     vlc_cond_t          wait_room; /**< Wait for queue depth to shrink */
466
467     block_t             *p_first;
468     block_t             **pp_last;
469     size_t              i_depth;
470     size_t              i_size;
471     bool          b_force_wake;
472 };
473
474 block_fifo_t *block_FifoNew( void )
475 {
476     block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
477     if( !p_fifo )
478         return NULL;
479
480     vlc_mutex_init( &p_fifo->lock );
481     vlc_cond_init( &p_fifo->wait );
482     vlc_cond_init( &p_fifo->wait_room );
483     p_fifo->p_first = NULL;
484     p_fifo->pp_last = &p_fifo->p_first;
485     p_fifo->i_depth = p_fifo->i_size = 0;
486     p_fifo->b_force_wake = false;
487
488     return p_fifo;
489 }
490
491 void block_FifoRelease( block_fifo_t *p_fifo )
492 {
493     block_FifoEmpty( p_fifo );
494     vlc_cond_destroy( &p_fifo->wait_room );
495     vlc_cond_destroy( &p_fifo->wait );
496     vlc_mutex_destroy( &p_fifo->lock );
497     free( p_fifo );
498 }
499
500 void block_FifoEmpty( block_fifo_t *p_fifo )
501 {
502     block_t *block;
503
504     vlc_mutex_lock( &p_fifo->lock );
505     block = p_fifo->p_first;
506     if (block != NULL)
507     {
508         p_fifo->i_depth = p_fifo->i_size = 0;
509         p_fifo->p_first = NULL;
510         p_fifo->pp_last = &p_fifo->p_first;
511     }
512     vlc_cond_broadcast( &p_fifo->wait_room );
513     vlc_mutex_unlock( &p_fifo->lock );
514
515     while (block != NULL)
516     {
517         block_t *buf;
518
519         buf = block->p_next;
520         block_Release (block);
521         block = buf;
522     }
523 }
524
525 /**
526  * Wait until the FIFO gets below a certain size (if needed).
527  *
528  * Note that if more than one thread writes to the FIFO, you cannot assume that
529  * the FIFO is actually below the requested size upon return (since another
530  * thread could have refilled it already). This is typically not an issue, as
531  * this function is meant for (relaxed) congestion control.
532  *
533  * This function may be a cancellation point and it is cancel-safe.
534  *
535  * @param fifo queue to wait on
536  * @param max_depth wait until the queue has no more than this many blocks
537  *                  (use SIZE_MAX to ignore this constraint)
538  * @param max_size wait until the queue has no more than this many bytes
539  *                  (use SIZE_MAX to ignore this constraint)
540  * @return nothing.
541  */
542 void block_FifoPace (block_fifo_t *fifo, size_t max_depth, size_t max_size)
543 {
544     vlc_testcancel ();
545
546     vlc_mutex_lock (&fifo->lock);
547     while ((fifo->i_depth > max_depth) || (fifo->i_size > max_size))
548     {
549          mutex_cleanup_push (&fifo->lock);
550          vlc_cond_wait (&fifo->wait_room, &fifo->lock);
551          vlc_cleanup_pop ();
552     }
553     vlc_mutex_unlock (&fifo->lock);
554 }
555
556 /**
557  * Immediately queue one block at the end of a FIFO.
558  * @param fifo queue
559  * @param block head of a block list to queue (may be NULL)
560  * @return total number of bytes appended to the queue
561  */
562 size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
563 {
564     size_t i_size = 0, i_depth = 0;
565     block_t *p_last;
566
567     if (p_block == NULL)
568         return 0;
569     for (p_last = p_block; ; p_last = p_last->p_next)
570     {
571         i_size += p_last->i_buffer;
572         i_depth++;
573         if (!p_last->p_next)
574             break;
575     }
576
577     vlc_mutex_lock (&p_fifo->lock);
578     *p_fifo->pp_last = p_block;
579     p_fifo->pp_last = &p_last->p_next;
580     p_fifo->i_depth += i_depth;
581     p_fifo->i_size += i_size;
582     /* We queued at least one block: wake up one read-waiting thread */
583     vlc_cond_signal( &p_fifo->wait );
584     vlc_mutex_unlock( &p_fifo->lock );
585
586     return i_size;
587 }
588
589 void block_FifoWake( block_fifo_t *p_fifo )
590 {
591     vlc_mutex_lock( &p_fifo->lock );
592     if( p_fifo->p_first == NULL )
593         p_fifo->b_force_wake = true;
594     vlc_cond_broadcast( &p_fifo->wait );
595     vlc_mutex_unlock( &p_fifo->lock );
596 }
597
598 /**
599  * Dequeue the first block from the FIFO. If necessary, wait until there is
600  * one block in the queue. This function is (always) cancellation point.
601  *
602  * @return a valid block, or NULL if block_FifoWake() was called.
603  */
604 block_t *block_FifoGet( block_fifo_t *p_fifo )
605 {
606     block_t *b;
607
608     vlc_testcancel( );
609
610     vlc_mutex_lock( &p_fifo->lock );
611     mutex_cleanup_push( &p_fifo->lock );
612
613     /* Remember vlc_cond_wait() may cause spurious wakeups
614      * (on both Win32 and POSIX) */
615     while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake )
616         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
617
618     vlc_cleanup_pop();
619     b = p_fifo->p_first;
620
621     p_fifo->b_force_wake = false;
622     if( b == NULL )
623     {
624         /* Forced wakeup */
625         vlc_mutex_unlock( &p_fifo->lock );
626         return NULL;
627     }
628
629     p_fifo->p_first = b->p_next;
630     p_fifo->i_depth--;
631     p_fifo->i_size -= b->i_buffer;
632
633     if( p_fifo->p_first == NULL )
634     {
635         p_fifo->pp_last = &p_fifo->p_first;
636     }
637
638     /* We don't know how many threads can queue new packets now. */
639     vlc_cond_broadcast( &p_fifo->wait_room );
640     vlc_mutex_unlock( &p_fifo->lock );
641
642     b->p_next = NULL;
643     return b;
644 }
645
646 /**
647  * Peeks the first block in the FIFO.
648  * If necessary, wait until there is one block.
649  * This function is (always) a cancellation point.
650  *
651  * @warning This function leaves the block in the FIFO.
652  * You need to protect against concurrent threads who could dequeue the block.
653  * Preferrably, there should be only one thread reading from the FIFO.
654  *
655  * @return a valid block.
656  */
657 block_t *block_FifoShow( block_fifo_t *p_fifo )
658 {
659     block_t *b;
660
661     vlc_testcancel( );
662
663     vlc_mutex_lock( &p_fifo->lock );
664     mutex_cleanup_push( &p_fifo->lock );
665
666     while( p_fifo->p_first == NULL )
667         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
668
669     b = p_fifo->p_first;
670
671     vlc_cleanup_run ();
672     return b;
673 }
674
675 /* FIXME: not thread-safe */
676 size_t block_FifoSize( const block_fifo_t *p_fifo )
677 {
678     return p_fifo->i_size;
679 }
680
681 /* FIXME: not thread-safe */
682 size_t block_FifoCount( const block_fifo_t *p_fifo )
683 {
684     return p_fifo->i_depth;
685 }