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