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