]> git.sesse.net Git - vlc/blob - include/vlc_threads.h
Merge vlc_threads.h and vlc_threads_funcs.h
[vlc] / include / vlc_threads.h
1 /*****************************************************************************
2  * vlc_threads.h : threads implementation for the VideoLAN client
3  * This header provides portable declarations for mutexes & conditions
4  *****************************************************************************
5  * Copyright (C) 1999, 2002 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@via.ecp.fr>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *          Christophe Massiot <massiot@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #if !defined( __LIBVLC__ )
29   #error You are not libvlc or one of its plugins. You cannot include this file
30 #endif
31
32 #ifndef _VLC_THREADS_H_
33 #define _VLC_THREADS_H_
34
35 #if defined( UNDER_CE )
36                                                                 /* WinCE API */
37 #elif defined( WIN32 )
38 #   include <process.h>                                         /* Win32 API */
39 #   include <errno.h>
40
41 #elif defined( HAVE_KERNEL_SCHEDULER_H )                             /* BeOS */
42 #   include <kernel/OS.h>
43 #   include <kernel/scheduler.h>
44 #   include <byteorder.h>
45
46 #else                                         /* pthreads (like Linux & BSD) */
47 #   define LIBVLC_USE_PTHREAD 1
48 #   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
49
50 #   include <unistd.h> /* _POSIX_SPIN_LOCKS */
51 #   include <pthread.h>
52     /* Needed for pthread_cond_timedwait */
53 #   include <errno.h>
54 #   include <time.h>
55
56 #endif
57
58 /*****************************************************************************
59  * Constants
60  *****************************************************************************/
61
62 /* Thread priorities */
63 #ifdef __APPLE__
64 #   define VLC_THREAD_PRIORITY_LOW (-47)
65 #   define VLC_THREAD_PRIORITY_INPUT 37
66 #   define VLC_THREAD_PRIORITY_AUDIO 37
67 #   define VLC_THREAD_PRIORITY_VIDEO (-47)
68 #   define VLC_THREAD_PRIORITY_OUTPUT 37
69 #   define VLC_THREAD_PRIORITY_HIGHEST 37
70
71 #elif defined(SYS_BEOS)
72 #   define VLC_THREAD_PRIORITY_LOW 5
73 #   define VLC_THREAD_PRIORITY_INPUT 10
74 #   define VLC_THREAD_PRIORITY_AUDIO 10
75 #   define VLC_THREAD_PRIORITY_VIDEO 5
76 #   define VLC_THREAD_PRIORITY_OUTPUT 15
77 #   define VLC_THREAD_PRIORITY_HIGHEST 15
78
79 #elif defined(LIBVLC_USE_PTHREAD)
80 #   define VLC_THREAD_PRIORITY_LOW 0
81 #   define VLC_THREAD_PRIORITY_INPUT 20
82 #   define VLC_THREAD_PRIORITY_AUDIO 10
83 #   define VLC_THREAD_PRIORITY_VIDEO 0
84 #   define VLC_THREAD_PRIORITY_OUTPUT 30
85 #   define VLC_THREAD_PRIORITY_HIGHEST 40
86
87 #elif defined(WIN32) || defined(UNDER_CE)
88 /* Define different priorities for WinNT/2K/XP and Win9x/Me */
89 #   define VLC_THREAD_PRIORITY_LOW 0
90 #   define VLC_THREAD_PRIORITY_INPUT \
91         (IS_WINNT ? THREAD_PRIORITY_ABOVE_NORMAL : 0)
92 #   define VLC_THREAD_PRIORITY_AUDIO \
93         (IS_WINNT ? THREAD_PRIORITY_HIGHEST : 0)
94 #   define VLC_THREAD_PRIORITY_VIDEO \
95         (IS_WINNT ? 0 : THREAD_PRIORITY_BELOW_NORMAL )
96 #   define VLC_THREAD_PRIORITY_OUTPUT \
97         (IS_WINNT ? THREAD_PRIORITY_ABOVE_NORMAL : 0)
98 #   define VLC_THREAD_PRIORITY_HIGHEST \
99         (IS_WINNT ? THREAD_PRIORITY_TIME_CRITICAL : 0)
100
101 #else
102 #   define VLC_THREAD_PRIORITY_LOW 0
103 #   define VLC_THREAD_PRIORITY_INPUT 0
104 #   define VLC_THREAD_PRIORITY_AUDIO 0
105 #   define VLC_THREAD_PRIORITY_VIDEO 0
106 #   define VLC_THREAD_PRIORITY_OUTPUT 0
107 #   define VLC_THREAD_PRIORITY_HIGHEST 0
108
109 #endif
110
111 /*****************************************************************************
112  * Type definitions
113  *****************************************************************************/
114
115 #if defined (LIBVLC_USE_PTHREAD)
116 typedef pthread_t       vlc_thread_t;
117 typedef pthread_mutex_t vlc_mutex_t;
118 typedef pthread_cond_t  vlc_cond_t;
119 typedef pthread_key_t   vlc_threadvar_t;
120
121 #elif defined( WIN32 ) || defined( UNDER_CE )
122 typedef struct
123 {
124     /* thread id */
125     DWORD  id;
126     /*
127     ** handle to created thread, needs be closed to dispose of it
128     ** even after thread has exited
129     */
130     HANDLE hThread;
131 } vlc_thread_t;
132
133 typedef BOOL (WINAPI *SIGNALOBJECTANDWAIT) ( HANDLE, HANDLE, DWORD, BOOL );
134
135 typedef HANDLE  vlc_mutex_t;
136
137 typedef struct
138 {
139     volatile int        i_waiting_threads;
140     HANDLE              event;
141 } vlc_cond_t;
142
143 typedef DWORD   vlc_threadvar_t;
144
145 #elif defined( HAVE_KERNEL_SCHEDULER_H )
146 /* This is the BeOS implementation of the vlc threads, note that the mutex is
147  * not a real mutex and the cond_var is not like a pthread cond_var but it is
148  * enough for what we need */
149
150 typedef thread_id vlc_thread_t;
151
152 typedef struct
153 {
154     int32_t         init;
155     sem_id          lock;
156 } vlc_mutex_t;
157
158 typedef struct
159 {
160     int32_t         init;
161     thread_id       thread;
162 } vlc_cond_t;
163
164 typedef struct
165 {
166 } vlc_threadvar_t;
167
168 #endif
169
170 #if defined( WIN32 ) && !defined ETIMEDOUT
171 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
172 #endif
173
174 /*****************************************************************************
175  * Function definitions
176  *****************************************************************************/
177 VLC_EXPORT( int,  vlc_mutex_init,    ( vlc_mutex_t * ) );
178 VLC_EXPORT( int,  vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
179 VLC_EXPORT( void,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
180 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_cond_t * ) );
181 VLC_EXPORT( void,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
182 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_threadvar_t * ) );
183 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, bool ) );
184 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
185 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
186 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
187
188 /*****************************************************************************
189  * vlc_mutex_lock: lock a mutex
190  *****************************************************************************/
191 #define vlc_mutex_lock( P_MUTEX )                                           \
192     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
193
194 #if defined(LIBVLC_USE_PTHREAD)
195 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
196
197 # define VLC_THREAD_ASSERT( action ) \
198     if (val) \
199         vlc_pthread_fatal (action, val, psz_file, i_line)
200 #else
201 # define VLC_THREAD_ASSERT (void)0
202 #endif
203
204 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
205                                     vlc_mutex_t * p_mutex )
206 {
207 #if defined(LIBVLC_USE_PTHREAD)
208 #   define vlc_assert_locked( m ) \
209            assert (pthread_mutex_lock (m) == EDEADLK)
210     int val = pthread_mutex_lock( p_mutex );
211     VLC_THREAD_ASSERT ("locking mutex");
212
213 #elif defined( UNDER_CE )
214     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
215
216     EnterCriticalSection( &p_mutex->csection );
217
218 #elif defined( WIN32 )
219     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
220
221     WaitForSingleObject( *p_mutex, INFINITE );
222
223 #elif defined( HAVE_KERNEL_SCHEDULER_H )
224     acquire_sem( p_mutex->lock );
225
226 #endif
227 }
228
229 #ifndef vlc_assert_locked
230 # define vlc_assert_locked( m ) (void)0
231 #endif
232
233 /*****************************************************************************
234  * vlc_mutex_unlock: unlock a mutex
235  *****************************************************************************/
236 #define vlc_mutex_unlock( P_MUTEX )                                         \
237     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
238
239 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
240                                       vlc_mutex_t *p_mutex )
241 {
242 #if defined(LIBVLC_USE_PTHREAD)
243     int val = pthread_mutex_unlock( p_mutex );
244     VLC_THREAD_ASSERT ("unlocking mutex");
245
246 #elif defined( UNDER_CE )
247     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
248
249     LeaveCriticalSection( &p_mutex->csection );
250
251 #elif defined( WIN32 )
252     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
253
254     ReleaseMutex( *p_mutex );
255
256 #elif defined( HAVE_KERNEL_SCHEDULER_H )
257     release_sem( p_mutex->lock );
258
259 #endif
260 }
261
262 /*****************************************************************************
263  * vlc_mutex_destroy: destroy a mutex
264  *****************************************************************************/
265 #define vlc_mutex_destroy( P_MUTEX )                                        \
266     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
267
268 /*****************************************************************************
269  * vlc_cond_init: initialize a condition
270  *****************************************************************************/
271 #define vlc_cond_init( P_THIS, P_COND )                                     \
272     __vlc_cond_init( P_COND )
273
274 /*****************************************************************************
275  * vlc_cond_signal: start a thread on condition completion
276  *****************************************************************************/
277 #define vlc_cond_signal( P_COND )                                           \
278     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
279
280 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
281                                       vlc_cond_t *p_condvar )
282 {
283 #if defined(LIBVLC_USE_PTHREAD)
284     int val = pthread_cond_signal( p_condvar );
285     VLC_THREAD_ASSERT ("signaling condition variable");
286
287 #elif defined( UNDER_CE ) || defined( WIN32 )
288     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
289
290     /* Release one waiting thread if one is available. */
291     /* For this trick to work properly, the vlc_cond_signal must be surrounded
292      * by a mutex. This will prevent another thread from stealing the signal */
293     /* PulseEvent() only works if none of the waiting threads is suspended.
294      * This is particularily problematic under a debug session.
295      * as documented in http://support.microsoft.com/kb/q173260/ */
296     PulseEvent( p_condvar->event );
297
298 #elif defined( HAVE_KERNEL_SCHEDULER_H )
299     while( p_condvar->thread != -1 )
300     {
301         thread_info info;
302         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
303             return;
304
305         if( info.state != B_THREAD_SUSPENDED )
306         {
307             /* The  waiting thread is not suspended so it could
308              * have been interrupted beetwen the unlock and the
309              * suspend_thread line. That is why we sleep a little
310              * before retesting p_condver->thread. */
311             snooze( 10000 );
312         }
313         else
314         {
315             /* Ok, we have to wake up that thread */
316             resume_thread( p_condvar->thread );
317         }
318     }
319
320 #endif
321 }
322
323 /*****************************************************************************
324  * vlc_cond_wait: wait until condition completion
325  *****************************************************************************/
326 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
327     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
328
329 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
330                                     vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
331 {
332 #if defined(LIBVLC_USE_PTHREAD)
333     int val = pthread_cond_wait( p_condvar, p_mutex );
334     VLC_THREAD_ASSERT ("waiting on condition");
335
336 #elif defined( UNDER_CE )
337     p_condvar->i_waiting_threads++;
338     LeaveCriticalSection( &p_mutex->csection );
339     WaitForSingleObject( p_condvar->event, INFINITE );
340     p_condvar->i_waiting_threads--;
341
342     /* Reacquire the mutex before returning. */
343     vlc_mutex_lock( p_mutex );
344
345 #elif defined( WIN32 )
346     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
347
348     /* Increase our wait count */
349     p_condvar->i_waiting_threads++;
350     SignalObjectAndWait( *p_mutex, p_condvar->event, INFINITE, FALSE );
351     p_condvar->i_waiting_threads--;
352
353     /* Reacquire the mutex before returning. */
354     vlc_mutex_lock( p_mutex );
355
356 #elif defined( HAVE_KERNEL_SCHEDULER_H )
357     /* The p_condvar->thread var is initialized before the unlock because
358      * it enables to identify when the thread is interrupted beetwen the
359      * unlock line and the suspend_thread line */
360     p_condvar->thread = find_thread( NULL );
361     vlc_mutex_unlock( p_mutex );
362     suspend_thread( p_condvar->thread );
363     p_condvar->thread = -1;
364
365     vlc_mutex_lock( p_mutex );
366
367 #endif
368 }
369
370
371 /*****************************************************************************
372  * vlc_cond_timedwait: wait until condition completion or expiration
373  *****************************************************************************
374  * Returns 0 if object signaled, an error code in case of timeout or error.
375  *****************************************************************************/
376 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE )                      \
377     __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE  )
378
379 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
380                                         vlc_cond_t *p_condvar,
381                                         vlc_mutex_t *p_mutex,
382                                         mtime_t deadline )
383 {
384 #if defined(LIBVLC_USE_PTHREAD)
385     lldiv_t d = lldiv( deadline, 1000000 );
386     struct timespec ts = { d.quot, d.rem * 1000 };
387
388     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
389     if (val == ETIMEDOUT)
390         return ETIMEDOUT; /* this error is perfectly normal */
391     VLC_THREAD_ASSERT ("timed-waiting on condition");
392
393 #elif defined( UNDER_CE )
394     mtime_t delay_ms = (deadline - mdate())/1000;
395
396     DWORD result;
397     if( delay_ms < 0 )
398         delay_ms = 0;
399
400     p_condvar->i_waiting_threads++;
401     LeaveCriticalSection( &p_mutex->csection );
402     result = WaitForSingleObject( p_condvar->event, delay_ms );
403     p_condvar->i_waiting_threads--;
404
405     /* Reacquire the mutex before returning. */
406     vlc_mutex_lock( p_mutex );
407
408     if(result == WAIT_TIMEOUT)
409        return ETIMEDOUT; /* this error is perfectly normal */
410
411 #elif defined( WIN32 )
412     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
413
414     DWORD result;
415
416     mtime_t delay_ms = (deadline - mdate())/1000;
417     if( delay_ms < 0 )
418         delay_ms = 0;
419
420     /* Increase our wait count */
421     p_condvar->i_waiting_threads++;
422     result = SignalObjectAndWait( *p_mutex, p_condvar->event,
423                                   delay_ms, FALSE );
424     p_condvar->i_waiting_threads--;
425
426     /* Reacquire the mutex before returning. */
427     vlc_mutex_lock( p_mutex );
428     if(result == WAIT_TIMEOUT)
429        return ETIMEDOUT; /* this error is perfectly normal */
430
431 #elif defined( HAVE_KERNEL_SCHEDULER_H )
432 #   error Unimplemented
433
434 #endif
435
436     return 0;
437 }
438
439 /*****************************************************************************
440  * vlc_cond_destroy: destroy a condition
441  *****************************************************************************/
442 #define vlc_cond_destroy( P_COND )                                          \
443     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
444
445 /*****************************************************************************
446  * vlc_threadvar_create: create a thread-local variable
447  *****************************************************************************/
448 #define vlc_threadvar_create( PTHIS, P_TLS )                                 \
449    __vlc_threadvar_create( P_TLS )
450
451 /*****************************************************************************
452  * vlc_threadvar_set: create: set the value of a thread-local variable
453  *****************************************************************************/
454 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
455 {
456     int i_ret;
457
458 #if defined(LIBVLC_USE_PTHREAD)
459     i_ret = pthread_setspecific( *p_tls, p_value );
460
461 #elif defined( HAVE_KERNEL_SCHEDULER_H )
462     i_ret = EINVAL;
463
464 #elif defined( UNDER_CE ) || defined( WIN32 )
465     i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0;
466
467 #endif
468
469     return i_ret;
470 }
471
472 /*****************************************************************************
473  * vlc_threadvar_get: create: get the value of a thread-local variable
474  *****************************************************************************/
475 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
476 {
477     void *p_ret;
478
479 #if defined(LIBVLC_USE_PTHREAD)
480     p_ret = pthread_getspecific( *p_tls );
481
482 #elif defined( HAVE_KERNEL_SCHEDULER_H )
483     p_ret = NULL;
484
485 #elif defined( UNDER_CE ) || defined( WIN32 )
486     p_ret = TlsGetValue( *p_tls );
487
488 #endif
489
490     return p_ret;
491 }
492
493 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
494 typedef pthread_spinlock_t vlc_spinlock_t;
495
496 /**
497  * Initializes a spinlock.
498  */
499 static inline int vlc_spin_init (vlc_spinlock_t *spin)
500 {
501     return pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE);
502 }
503
504 /**
505  * Acquires a spinlock.
506  */
507 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
508 {
509     pthread_spin_lock (spin);
510 }
511
512 /**
513  * Releases a spinlock.
514  */
515 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
516 {
517     pthread_spin_unlock (spin);
518 }
519
520 /**
521  * Deinitializes a spinlock.
522  */
523 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
524 {
525     pthread_spin_destroy (spin);
526 }
527
528 #elif defined( WIN32 )
529
530 typedef CRITICAL_SECTION vlc_spinlock_t;
531
532 /**
533  * Initializes a spinlock.
534  */
535 static inline int vlc_spin_init (vlc_spinlock_t *spin)
536 {
537     return !InitializeCriticalSectionAndSpinCount(spin, 4000);
538 }
539
540 /**
541  * Acquires a spinlock.
542  */
543 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
544 {
545     EnterCriticalSection(spin);
546 }
547
548 /**
549  * Releases a spinlock.
550  */
551 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
552 {
553     LeaveCriticalSection(spin);
554 }
555
556 /**
557  * Deinitializes a spinlock.
558  */
559 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
560 {
561     DeleteCriticalSection(spin);
562 }
563
564 #else
565
566 /* Fallback to plain mutexes if spinlocks are not available */
567 typedef vlc_mutex_t vlc_spinlock_t;
568
569 static inline int vlc_spin_init (vlc_spinlock_t *spin)
570 {
571     return vlc_mutex_init (spin);
572 }
573
574 # define vlc_spin_lock    vlc_mutex_lock
575 # define vlc_spin_unlock  vlc_mutex_unlock
576 # define vlc_spin_destroy vlc_mutex_destroy
577 #endif
578
579 /*****************************************************************************
580  * vlc_thread_create: create a thread
581  *****************************************************************************/
582 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
583     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
584
585 /*****************************************************************************
586  * vlc_thread_set_priority: set the priority of the calling thread
587  *****************************************************************************/
588 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
589     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
590
591 /*****************************************************************************
592  * vlc_thread_ready: tell the parent thread we were successfully spawned
593  *****************************************************************************/
594 #define vlc_thread_ready( P_THIS )                                          \
595     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
596
597 /*****************************************************************************
598  * vlc_thread_join: wait until a thread exits
599  *****************************************************************************/
600 #define vlc_thread_join( P_THIS )                                           \
601     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )
602
603 #endif /* !_VLC_THREADS_H */