]> git.sesse.net Git - vlc/blob - src/misc/block.c
Add block_shm_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 #ifdef HAVE_SYS_SHM_H
339 # include <sys/shm.h>
340
341 typedef struct block_shm_t
342 {
343     block_t     self;
344     void       *base_addr;
345 } block_shm_t;
346
347 static void block_shm_Release (block_t *block)
348 {
349     block_shm_t *p_sys = (block_shm_t *)block;
350
351     shmdt (p_sys->base_addr);
352     free (p_sys);
353 }
354
355 /**
356  * Creates a block from a System V shared memory segment (shmget()).
357  * This is provided by LibVLC so that segments can safely be deallocated
358  * even after the allocating plugin has been unloaded from memory.
359  *
360  * @param addr base address of the segment (as returned by shmat())
361  * @param length length (bytes) of the segment (as passed to shmget())
362  * @return NULL if an error occurred (in that case, shmdt(addr) is invoked
363  * before returning NULL).
364  */
365 block_t *block_shm_Alloc (void *addr, size_t length)
366 {
367     block_shm_t *block = malloc (sizeof (*block));
368     if (unlikely(block == NULL))
369     {
370         shmdt (addr);
371         return NULL;
372     }
373
374     block_Init (&block->self, (uint8_t *)addr, length);
375     block->self.pf_release = block_shm_Release;
376     block->base_addr = addr;
377     return &block->self;
378 }
379 #else
380 block_t *block_shm_Alloc (void *addr, size_t length)
381 {
382     (void) addr; (void) length;
383     abort ();
384 }
385 #endif
386
387
388 #ifdef WIN32
389 # include <io.h>
390
391 static
392 ssize_t pread (int fd, void *buf, size_t count, off_t offset)
393 {
394     HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd);
395     if (handle == INVALID_HANDLE_VALUE)
396         return -1;
397
398     OVERLAPPED olap; olap.Offset = offset; olap.OffsetHigh = (offset >> 32);
399     DWORD written;
400     /* This braindead API will override the file pointer even if we specify
401      * an explicit read offset... So do not expect this to mix well with
402      * regular read() calls. */
403     if (ReadFile (handle, buf, count, &written, &olap))
404         return written;
405     return -1;
406 }
407 #endif
408
409 /**
410  * Loads a file into a block of memory through a file descriptor.
411  * If possible a private file mapping is created. Otherwise, the file is read
412  * normally. This function is a cancellation point.
413  *
414  * @note On 32-bits platforms,
415  * this function will not work for very large files,
416  * due to memory space constraints.
417  *
418  * @param fd file descriptor to load from
419  * @return a new block with the file content at p_buffer, and file length at
420  * i_buffer (release it with block_Release()), or NULL upon error (see errno).
421  */
422 block_t *block_File (int fd)
423 {
424     size_t length;
425     struct stat st;
426
427     /* First, get the file size */
428     if (fstat (fd, &st))
429         return NULL;
430
431     /* st_size is meaningful for regular files, shared memory and typed memory.
432      * It's also meaning for symlinks, but that's not possible with fstat().
433      * In other cases, it's undefined, and we should really not go further. */
434 #ifndef S_TYPEISSHM
435 # define S_TYPEISSHM( buf ) (0)
436 #endif
437     if (S_ISDIR (st.st_mode))
438     {
439         errno = EISDIR;
440         return NULL;
441     }
442     if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st))
443     {
444         errno = ESPIPE;
445         return NULL;
446     }
447
448     /* Prevent an integer overflow in mmap() and malloc() */
449     if (st.st_size >= SIZE_MAX)
450     {
451         errno = ENOMEM;
452         return NULL;
453     }
454     length = (size_t)st.st_size;
455
456 #ifdef HAVE_MMAP
457     if (length > 0)
458     {
459         void *addr;
460
461         addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
462         if (addr != MAP_FAILED)
463             return block_mmap_Alloc (addr, length);
464     }
465 #endif
466
467     /* If mmap() is not implemented by the OS _or_ the filesystem... */
468     block_t *block = block_Alloc (length);
469     if (block == NULL)
470         return NULL;
471     block_cleanup_push (block);
472
473     for (size_t i = 0; i < length;)
474     {
475         ssize_t len = pread (fd, block->p_buffer + i, length - i, i);
476         if (len == -1)
477         {
478             block_Release (block);
479             block = NULL;
480             break;
481         }
482         i += len;
483     }
484     vlc_cleanup_pop ();
485     return block;
486 }
487
488 /**
489  * Loads a file into a block of memory from the file path.
490  * See also block_File().
491  */
492 block_t *block_FilePath (const char *path)
493 {
494     int fd = vlc_open (path, O_RDONLY);
495     if (fd == -1)
496         return NULL;
497
498     block_t *block = block_File (fd);
499     close (fd);
500     return block;
501 }
502
503 /**
504  * @section Thread-safe block queue functions
505  */
506
507 /**
508  * Internal state for block queues
509  */
510 struct block_fifo_t
511 {
512     vlc_mutex_t         lock;                         /* fifo data lock */
513     vlc_cond_t          wait;      /**< Wait for data */
514     vlc_cond_t          wait_room; /**< Wait for queue depth to shrink */
515
516     block_t             *p_first;
517     block_t             **pp_last;
518     size_t              i_depth;
519     size_t              i_size;
520     bool          b_force_wake;
521 };
522
523 block_fifo_t *block_FifoNew( void )
524 {
525     block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) );
526     if( !p_fifo )
527         return NULL;
528
529     vlc_mutex_init( &p_fifo->lock );
530     vlc_cond_init( &p_fifo->wait );
531     vlc_cond_init( &p_fifo->wait_room );
532     p_fifo->p_first = NULL;
533     p_fifo->pp_last = &p_fifo->p_first;
534     p_fifo->i_depth = p_fifo->i_size = 0;
535     p_fifo->b_force_wake = false;
536
537     return p_fifo;
538 }
539
540 void block_FifoRelease( block_fifo_t *p_fifo )
541 {
542     block_FifoEmpty( p_fifo );
543     vlc_cond_destroy( &p_fifo->wait_room );
544     vlc_cond_destroy( &p_fifo->wait );
545     vlc_mutex_destroy( &p_fifo->lock );
546     free( p_fifo );
547 }
548
549 void block_FifoEmpty( block_fifo_t *p_fifo )
550 {
551     block_t *block;
552
553     vlc_mutex_lock( &p_fifo->lock );
554     block = p_fifo->p_first;
555     if (block != NULL)
556     {
557         p_fifo->i_depth = p_fifo->i_size = 0;
558         p_fifo->p_first = NULL;
559         p_fifo->pp_last = &p_fifo->p_first;
560     }
561     vlc_cond_broadcast( &p_fifo->wait_room );
562     vlc_mutex_unlock( &p_fifo->lock );
563
564     while (block != NULL)
565     {
566         block_t *buf;
567
568         buf = block->p_next;
569         block_Release (block);
570         block = buf;
571     }
572 }
573
574 /**
575  * Wait until the FIFO gets below a certain size (if needed).
576  *
577  * Note that if more than one thread writes to the FIFO, you cannot assume that
578  * the FIFO is actually below the requested size upon return (since another
579  * thread could have refilled it already). This is typically not an issue, as
580  * this function is meant for (relaxed) congestion control.
581  *
582  * This function may be a cancellation point and it is cancel-safe.
583  *
584  * @param fifo queue to wait on
585  * @param max_depth wait until the queue has no more than this many blocks
586  *                  (use SIZE_MAX to ignore this constraint)
587  * @param max_size wait until the queue has no more than this many bytes
588  *                  (use SIZE_MAX to ignore this constraint)
589  * @return nothing.
590  */
591 void block_FifoPace (block_fifo_t *fifo, size_t max_depth, size_t max_size)
592 {
593     vlc_testcancel ();
594
595     vlc_mutex_lock (&fifo->lock);
596     while ((fifo->i_depth > max_depth) || (fifo->i_size > max_size))
597     {
598          mutex_cleanup_push (&fifo->lock);
599          vlc_cond_wait (&fifo->wait_room, &fifo->lock);
600          vlc_cleanup_pop ();
601     }
602     vlc_mutex_unlock (&fifo->lock);
603 }
604
605 /**
606  * Immediately queue one block at the end of a FIFO.
607  * @param fifo queue
608  * @param block head of a block list to queue (may be NULL)
609  * @return total number of bytes appended to the queue
610  */
611 size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block )
612 {
613     size_t i_size = 0, i_depth = 0;
614     block_t *p_last;
615
616     if (p_block == NULL)
617         return 0;
618     for (p_last = p_block; ; p_last = p_last->p_next)
619     {
620         i_size += p_last->i_buffer;
621         i_depth++;
622         if (!p_last->p_next)
623             break;
624     }
625
626     vlc_mutex_lock (&p_fifo->lock);
627     *p_fifo->pp_last = p_block;
628     p_fifo->pp_last = &p_last->p_next;
629     p_fifo->i_depth += i_depth;
630     p_fifo->i_size += i_size;
631     /* We queued at least one block: wake up one read-waiting thread */
632     vlc_cond_signal( &p_fifo->wait );
633     vlc_mutex_unlock( &p_fifo->lock );
634
635     return i_size;
636 }
637
638 void block_FifoWake( block_fifo_t *p_fifo )
639 {
640     vlc_mutex_lock( &p_fifo->lock );
641     if( p_fifo->p_first == NULL )
642         p_fifo->b_force_wake = true;
643     vlc_cond_broadcast( &p_fifo->wait );
644     vlc_mutex_unlock( &p_fifo->lock );
645 }
646
647 /**
648  * Dequeue the first block from the FIFO. If necessary, wait until there is
649  * one block in the queue. This function is (always) cancellation point.
650  *
651  * @return a valid block, or NULL if block_FifoWake() was called.
652  */
653 block_t *block_FifoGet( block_fifo_t *p_fifo )
654 {
655     block_t *b;
656
657     vlc_testcancel( );
658
659     vlc_mutex_lock( &p_fifo->lock );
660     mutex_cleanup_push( &p_fifo->lock );
661
662     /* Remember vlc_cond_wait() may cause spurious wakeups
663      * (on both Win32 and POSIX) */
664     while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake )
665         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
666
667     vlc_cleanup_pop();
668     b = p_fifo->p_first;
669
670     p_fifo->b_force_wake = false;
671     if( b == NULL )
672     {
673         /* Forced wakeup */
674         vlc_mutex_unlock( &p_fifo->lock );
675         return NULL;
676     }
677
678     p_fifo->p_first = b->p_next;
679     p_fifo->i_depth--;
680     p_fifo->i_size -= b->i_buffer;
681
682     if( p_fifo->p_first == NULL )
683     {
684         p_fifo->pp_last = &p_fifo->p_first;
685     }
686
687     /* We don't know how many threads can queue new packets now. */
688     vlc_cond_broadcast( &p_fifo->wait_room );
689     vlc_mutex_unlock( &p_fifo->lock );
690
691     b->p_next = NULL;
692     return b;
693 }
694
695 /**
696  * Peeks the first block in the FIFO.
697  * If necessary, wait until there is one block.
698  * This function is (always) a cancellation point.
699  *
700  * @warning This function leaves the block in the FIFO.
701  * You need to protect against concurrent threads who could dequeue the block.
702  * Preferrably, there should be only one thread reading from the FIFO.
703  *
704  * @return a valid block.
705  */
706 block_t *block_FifoShow( block_fifo_t *p_fifo )
707 {
708     block_t *b;
709
710     vlc_testcancel( );
711
712     vlc_mutex_lock( &p_fifo->lock );
713     mutex_cleanup_push( &p_fifo->lock );
714
715     while( p_fifo->p_first == NULL )
716         vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
717
718     b = p_fifo->p_first;
719
720     vlc_cleanup_run ();
721     return b;
722 }
723
724 /* FIXME: not thread-safe */
725 size_t block_FifoSize( const block_fifo_t *p_fifo )
726 {
727     return p_fifo->i_size;
728 }
729
730 /* FIXME: not thread-safe */
731 size_t block_FifoCount( const block_fifo_t *p_fifo )
732 {
733     return p_fifo->i_depth;
734 }