]> git.sesse.net Git - vlc/blob - include/vlc_threads.h
The TLS also needs to be cleaned up... should fix #1576
[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 HANDLE  vlc_thread_t;
123
124 typedef BOOL (WINAPI *SIGNALOBJECTANDWAIT) ( HANDLE, HANDLE, DWORD, BOOL );
125
126 typedef HANDLE  vlc_mutex_t;
127
128 typedef struct
129 {
130     volatile int        i_waiting_threads;
131     HANDLE              event;
132 } vlc_cond_t;
133
134 typedef DWORD   vlc_threadvar_t;
135
136 #elif defined( HAVE_KERNEL_SCHEDULER_H )
137 /* This is the BeOS implementation of the vlc threads, note that the mutex is
138  * not a real mutex and the cond_var is not like a pthread cond_var but it is
139  * enough for what we need */
140
141 typedef thread_id vlc_thread_t;
142
143 typedef struct
144 {
145     int32_t         init;
146     sem_id          lock;
147 } vlc_mutex_t;
148
149 typedef struct
150 {
151     int32_t         init;
152     thread_id       thread;
153 } vlc_cond_t;
154
155 typedef struct
156 {
157 } vlc_threadvar_t;
158
159 #endif
160
161 #if defined( WIN32 ) && !defined ETIMEDOUT
162 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
163 #endif
164
165 /*****************************************************************************
166  * Function definitions
167  *****************************************************************************/
168 VLC_EXPORT( int,  vlc_mutex_init,    ( vlc_mutex_t * ) );
169 VLC_EXPORT( int,  vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
170 VLC_EXPORT( void,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
171 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_cond_t * ) );
172 VLC_EXPORT( void,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
173 VLC_EXPORT( int, vlc_threadvar_create, (vlc_threadvar_t * , void (*) (void *) ) );
174 VLC_EXPORT( void, vlc_threadvar_delete, (vlc_threadvar_t *) );
175 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, bool ) );
176 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
177 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
178 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
179
180 /*****************************************************************************
181  * vlc_mutex_lock: lock a mutex
182  *****************************************************************************/
183 #define vlc_mutex_lock( P_MUTEX )                                           \
184     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
185
186 #if defined(LIBVLC_USE_PTHREAD)
187 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
188
189 # define VLC_THREAD_ASSERT( action ) \
190     if (val) \
191         vlc_pthread_fatal (action, val, psz_file, i_line)
192 #else
193 # define VLC_THREAD_ASSERT (void)0
194 #endif
195
196 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
197                                     vlc_mutex_t * p_mutex )
198 {
199 #if defined(LIBVLC_USE_PTHREAD)
200 #   define vlc_assert_locked( m ) \
201            assert (pthread_mutex_lock (m) == EDEADLK)
202     int val = pthread_mutex_lock( p_mutex );
203     VLC_THREAD_ASSERT ("locking mutex");
204
205 #elif defined( UNDER_CE )
206     (void)psz_file; (void)i_line;
207
208     EnterCriticalSection( &p_mutex->csection );
209
210 #elif defined( WIN32 )
211     (void)psz_file; (void)i_line;
212
213     WaitForSingleObject( *p_mutex, INFINITE );
214
215 #elif defined( HAVE_KERNEL_SCHEDULER_H )
216     acquire_sem( p_mutex->lock );
217
218 #endif
219 }
220
221 #ifndef vlc_assert_locked
222 # define vlc_assert_locked( m ) (void)0
223 #endif
224
225 /*****************************************************************************
226  * vlc_mutex_unlock: unlock a mutex
227  *****************************************************************************/
228 #define vlc_mutex_unlock( P_MUTEX )                                         \
229     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
230
231 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
232                                       vlc_mutex_t *p_mutex )
233 {
234 #if defined(LIBVLC_USE_PTHREAD)
235     int val = pthread_mutex_unlock( p_mutex );
236     VLC_THREAD_ASSERT ("unlocking mutex");
237
238 #elif defined( UNDER_CE )
239     (void)psz_file); (void)i_line;
240
241     LeaveCriticalSection( &p_mutex->csection );
242
243 #elif defined( WIN32 )
244     (void)psz_file; (void)i_line;
245
246     ReleaseMutex( *p_mutex );
247
248 #elif defined( HAVE_KERNEL_SCHEDULER_H )
249     release_sem( p_mutex->lock );
250
251 #endif
252 }
253
254 /*****************************************************************************
255  * vlc_mutex_destroy: destroy a mutex
256  *****************************************************************************/
257 #define vlc_mutex_destroy( P_MUTEX )                                        \
258     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
259
260 /*****************************************************************************
261  * vlc_cond_init: initialize a condition
262  *****************************************************************************/
263 #define vlc_cond_init( P_THIS, P_COND )                                     \
264     __vlc_cond_init( P_COND )
265
266 /*****************************************************************************
267  * vlc_cond_signal: start a thread on condition completion
268  *****************************************************************************/
269 #define vlc_cond_signal( P_COND )                                           \
270     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
271
272 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
273                                       vlc_cond_t *p_condvar )
274 {
275 #if defined(LIBVLC_USE_PTHREAD)
276     int val = pthread_cond_signal( p_condvar );
277     VLC_THREAD_ASSERT ("signaling condition variable");
278
279 #elif defined( UNDER_CE ) || defined( WIN32 )
280     (void)psz_file; (void)i_line;
281
282     /* Release one waiting thread if one is available. */
283     /* For this trick to work properly, the vlc_cond_signal must be surrounded
284      * by a mutex. This will prevent another thread from stealing the signal */
285     /* PulseEvent() only works if none of the waiting threads is suspended.
286      * This is particularily problematic under a debug session.
287      * as documented in http://support.microsoft.com/kb/q173260/ */
288     PulseEvent( p_condvar->event );
289
290 #elif defined( HAVE_KERNEL_SCHEDULER_H )
291     while( p_condvar->thread != -1 )
292     {
293         thread_info info;
294         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
295             return;
296
297         if( info.state != B_THREAD_SUSPENDED )
298         {
299             /* The  waiting thread is not suspended so it could
300              * have been interrupted beetwen the unlock and the
301              * suspend_thread line. That is why we sleep a little
302              * before retesting p_condver->thread. */
303             snooze( 10000 );
304         }
305         else
306         {
307             /* Ok, we have to wake up that thread */
308             resume_thread( p_condvar->thread );
309         }
310     }
311
312 #endif
313 }
314
315 /*****************************************************************************
316  * vlc_cond_wait: wait until condition completion
317  *****************************************************************************/
318 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
319     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
320
321 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
322                                     vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
323 {
324 #if defined(LIBVLC_USE_PTHREAD)
325     int val = pthread_cond_wait( p_condvar, p_mutex );
326     VLC_THREAD_ASSERT ("waiting on condition");
327
328 #elif defined( UNDER_CE )
329     p_condvar->i_waiting_threads++;
330     LeaveCriticalSection( &p_mutex->csection );
331     WaitForSingleObject( p_condvar->event, INFINITE );
332     p_condvar->i_waiting_threads--;
333
334     /* Reacquire the mutex before returning. */
335     vlc_mutex_lock( p_mutex );
336
337 #elif defined( WIN32 )
338     (void)psz_file; (void)i_line;
339
340     /* Increase our wait count */
341     p_condvar->i_waiting_threads++;
342     SignalObjectAndWait( *p_mutex, p_condvar->event, INFINITE, FALSE );
343     p_condvar->i_waiting_threads--;
344
345     /* Reacquire the mutex before returning. */
346     vlc_mutex_lock( p_mutex );
347
348 #elif defined( HAVE_KERNEL_SCHEDULER_H )
349     /* The p_condvar->thread var is initialized before the unlock because
350      * it enables to identify when the thread is interrupted beetwen the
351      * unlock line and the suspend_thread line */
352     p_condvar->thread = find_thread( NULL );
353     vlc_mutex_unlock( p_mutex );
354     suspend_thread( p_condvar->thread );
355     p_condvar->thread = -1;
356
357     vlc_mutex_lock( p_mutex );
358
359 #endif
360 }
361
362
363 /*****************************************************************************
364  * vlc_cond_timedwait: wait until condition completion or expiration
365  *****************************************************************************
366  * Returns 0 if object signaled, an error code in case of timeout or error.
367  *****************************************************************************/
368 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE )                      \
369     __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE  )
370
371 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
372                                         vlc_cond_t *p_condvar,
373                                         vlc_mutex_t *p_mutex,
374                                         mtime_t deadline )
375 {
376 #if defined(LIBVLC_USE_PTHREAD)
377     lldiv_t d = lldiv( deadline, 1000000 );
378     struct timespec ts = { d.quot, d.rem * 1000 };
379
380     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
381     if (val == ETIMEDOUT)
382         return ETIMEDOUT; /* this error is perfectly normal */
383     VLC_THREAD_ASSERT ("timed-waiting on condition");
384
385 #elif defined( UNDER_CE )
386     mtime_t delay_ms = (deadline - mdate())/1000;
387     DWORD result;
388     if( delay_ms < 0 )
389         delay_ms = 0;
390
391     p_condvar->i_waiting_threads++;
392     LeaveCriticalSection( &p_mutex->csection );
393     result = WaitForSingleObject( p_condvar->event, delay_ms );
394     p_condvar->i_waiting_threads--;
395
396     /* Reacquire the mutex before returning. */
397     vlc_mutex_lock( p_mutex );
398
399     if(result == WAIT_TIMEOUT)
400        return ETIMEDOUT; /* this error is perfectly normal */
401
402     (void)psz_file; (void)i_line;
403
404 #elif defined( WIN32 )
405     mtime_t delay_ms = (deadline - mdate())/1000;
406     DWORD result;
407     if( delay_ms < 0 )
408         delay_ms = 0;
409
410     /* Increase our wait count */
411     p_condvar->i_waiting_threads++;
412     result = SignalObjectAndWait( *p_mutex, p_condvar->event,
413                                   delay_ms, FALSE );
414     p_condvar->i_waiting_threads--;
415
416     /* Reacquire the mutex before returning. */
417     vlc_mutex_lock( p_mutex );
418     if(result == WAIT_TIMEOUT)
419        return ETIMEDOUT; /* this error is perfectly normal */
420
421     (void)psz_file; (void)i_line;
422
423 #elif defined( HAVE_KERNEL_SCHEDULER_H )
424 #   error Unimplemented
425
426 #endif
427
428     return 0;
429 }
430
431 /*****************************************************************************
432  * vlc_cond_destroy: destroy a condition
433  *****************************************************************************/
434 #define vlc_cond_destroy( P_COND )                                          \
435     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
436
437 /*****************************************************************************
438  * vlc_threadvar_set: create: set the value of a thread-local variable
439  *****************************************************************************/
440 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
441 {
442     int i_ret;
443
444 #if defined(LIBVLC_USE_PTHREAD)
445     i_ret = pthread_setspecific( *p_tls, p_value );
446
447 #elif defined( HAVE_KERNEL_SCHEDULER_H )
448     i_ret = EINVAL;
449
450 #elif defined( UNDER_CE ) || defined( WIN32 )
451     i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0;
452
453 #endif
454
455     return i_ret;
456 }
457
458 /*****************************************************************************
459  * vlc_threadvar_get: create: get the value of a thread-local variable
460  *****************************************************************************/
461 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
462 {
463     void *p_ret;
464
465 #if defined(LIBVLC_USE_PTHREAD)
466     p_ret = pthread_getspecific( *p_tls );
467
468 #elif defined( HAVE_KERNEL_SCHEDULER_H )
469     p_ret = NULL;
470
471 #elif defined( UNDER_CE ) || defined( WIN32 )
472     p_ret = TlsGetValue( *p_tls );
473
474 #endif
475
476     return p_ret;
477 }
478
479 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
480 typedef pthread_spinlock_t vlc_spinlock_t;
481
482 /**
483  * Initializes a spinlock.
484  */
485 static inline int vlc_spin_init (vlc_spinlock_t *spin)
486 {
487     return pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE);
488 }
489
490 /**
491  * Acquires a spinlock.
492  */
493 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
494 {
495     pthread_spin_lock (spin);
496 }
497
498 /**
499  * Releases a spinlock.
500  */
501 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
502 {
503     pthread_spin_unlock (spin);
504 }
505
506 /**
507  * Deinitializes a spinlock.
508  */
509 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
510 {
511     pthread_spin_destroy (spin);
512 }
513
514 #elif defined( WIN32 )
515
516 typedef CRITICAL_SECTION vlc_spinlock_t;
517
518 /**
519  * Initializes a spinlock.
520  */
521 static inline int vlc_spin_init (vlc_spinlock_t *spin)
522 {
523     return !InitializeCriticalSectionAndSpinCount(spin, 4000);
524 }
525
526 /**
527  * Acquires a spinlock.
528  */
529 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
530 {
531     EnterCriticalSection(spin);
532 }
533
534 /**
535  * Releases a spinlock.
536  */
537 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
538 {
539     LeaveCriticalSection(spin);
540 }
541
542 /**
543  * Deinitializes a spinlock.
544  */
545 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
546 {
547     DeleteCriticalSection(spin);
548 }
549
550 #else
551
552 /* Fallback to plain mutexes if spinlocks are not available */
553 typedef vlc_mutex_t vlc_spinlock_t;
554
555 static inline int vlc_spin_init (vlc_spinlock_t *spin)
556 {
557     return vlc_mutex_init (spin);
558 }
559
560 # define vlc_spin_lock    vlc_mutex_lock
561 # define vlc_spin_unlock  vlc_mutex_unlock
562 # define vlc_spin_destroy vlc_mutex_destroy
563 #endif
564
565 /*****************************************************************************
566  * vlc_thread_create: create a thread
567  *****************************************************************************/
568 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
569     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
570
571 /*****************************************************************************
572  * vlc_thread_set_priority: set the priority of the calling thread
573  *****************************************************************************/
574 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
575     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
576
577 /*****************************************************************************
578  * vlc_thread_ready: tell the parent thread we were successfully spawned
579  *****************************************************************************/
580 #define vlc_thread_ready( P_THIS )                                          \
581     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
582
583 /*****************************************************************************
584  * vlc_thread_join: wait until a thread exits
585  *****************************************************************************/
586 #define vlc_thread_join( P_THIS )                                           \
587     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )
588
589 #endif /* !_VLC_THREADS_H */