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
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>
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.
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.
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 *****************************************************************************/
28 #if !defined( __LIBVLC__ )
29 #error You are not libvlc or one of its plugins. You cannot include this file
32 #ifndef _VLC_THREADS_H_
33 #define _VLC_THREADS_H_
35 #if defined( UNDER_CE )
37 #elif defined( WIN32 )
38 # include <process.h> /* Win32 API */
41 #elif defined( HAVE_KERNEL_SCHEDULER_H ) /* BeOS */
42 # include <kernel/OS.h>
43 # include <kernel/scheduler.h>
44 # include <byteorder.h>
46 #else /* pthreads (like Linux & BSD) */
47 # define LIBVLC_USE_PTHREAD 1
48 # define _APPLE_C_SOURCE 1 /* Proper pthread semantics on OSX */
50 # include <unistd.h> /* _POSIX_SPIN_LOCKS */
52 /* Needed for pthread_cond_timedwait */
58 /*****************************************************************************
60 *****************************************************************************/
62 /* Thread priorities */
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
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
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
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)
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
111 /*****************************************************************************
113 *****************************************************************************/
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;
121 #elif defined( WIN32 ) || defined( UNDER_CE )
127 ** handle to created thread, needs be closed to dispose of it
128 ** even after thread has exited
133 typedef BOOL (WINAPI *SIGNALOBJECTANDWAIT) ( HANDLE, HANDLE, DWORD, BOOL );
135 typedef HANDLE vlc_mutex_t;
139 volatile int i_waiting_threads;
143 typedef DWORD vlc_threadvar_t;
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 */
150 typedef thread_id vlc_thread_t;
170 #if defined( WIN32 ) && !defined ETIMEDOUT
171 # define ETIMEDOUT 10060 /* This is the value in winsock.h. */
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 ) );
188 /*****************************************************************************
189 * vlc_mutex_lock: lock a mutex
190 *****************************************************************************/
191 #define vlc_mutex_lock( P_MUTEX ) \
192 __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
194 #if defined(LIBVLC_USE_PTHREAD)
195 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
197 # define VLC_THREAD_ASSERT( action ) \
199 vlc_pthread_fatal (action, val, psz_file, i_line)
201 # define VLC_THREAD_ASSERT (void)0
204 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
205 vlc_mutex_t * p_mutex )
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");
213 #elif defined( UNDER_CE )
214 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
216 EnterCriticalSection( &p_mutex->csection );
218 #elif defined( WIN32 )
219 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
221 WaitForSingleObject( *p_mutex, INFINITE );
223 #elif defined( HAVE_KERNEL_SCHEDULER_H )
224 acquire_sem( p_mutex->lock );
229 #ifndef vlc_assert_locked
230 # define vlc_assert_locked( m ) (void)0
233 /*****************************************************************************
234 * vlc_mutex_unlock: unlock a mutex
235 *****************************************************************************/
236 #define vlc_mutex_unlock( P_MUTEX ) \
237 __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
239 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
240 vlc_mutex_t *p_mutex )
242 #if defined(LIBVLC_USE_PTHREAD)
243 int val = pthread_mutex_unlock( p_mutex );
244 VLC_THREAD_ASSERT ("unlocking mutex");
246 #elif defined( UNDER_CE )
247 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
249 LeaveCriticalSection( &p_mutex->csection );
251 #elif defined( WIN32 )
252 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
254 ReleaseMutex( *p_mutex );
256 #elif defined( HAVE_KERNEL_SCHEDULER_H )
257 release_sem( p_mutex->lock );
262 /*****************************************************************************
263 * vlc_mutex_destroy: destroy a mutex
264 *****************************************************************************/
265 #define vlc_mutex_destroy( P_MUTEX ) \
266 __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
268 /*****************************************************************************
269 * vlc_cond_init: initialize a condition
270 *****************************************************************************/
271 #define vlc_cond_init( P_THIS, P_COND ) \
272 __vlc_cond_init( P_COND )
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 )
280 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
281 vlc_cond_t *p_condvar )
283 #if defined(LIBVLC_USE_PTHREAD)
284 int val = pthread_cond_signal( p_condvar );
285 VLC_THREAD_ASSERT ("signaling condition variable");
287 #elif defined( UNDER_CE ) || defined( WIN32 )
288 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
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 );
298 #elif defined( HAVE_KERNEL_SCHEDULER_H )
299 while( p_condvar->thread != -1 )
302 if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
305 if( info.state != B_THREAD_SUSPENDED )
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. */
315 /* Ok, we have to wake up that thread */
316 resume_thread( p_condvar->thread );
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 )
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 )
332 #if defined(LIBVLC_USE_PTHREAD)
333 int val = pthread_cond_wait( p_condvar, p_mutex );
334 VLC_THREAD_ASSERT ("waiting on condition");
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--;
342 /* Reacquire the mutex before returning. */
343 vlc_mutex_lock( p_mutex );
345 #elif defined( WIN32 )
346 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
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--;
353 /* Reacquire the mutex before returning. */
354 vlc_mutex_lock( p_mutex );
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;
365 vlc_mutex_lock( p_mutex );
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 )
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,
384 #if defined(LIBVLC_USE_PTHREAD)
385 lldiv_t d = lldiv( deadline, 1000000 );
386 struct timespec ts = { d.quot, d.rem * 1000 };
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");
393 #elif defined( UNDER_CE )
394 mtime_t delay_ms = (deadline - mdate())/1000;
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--;
405 /* Reacquire the mutex before returning. */
406 vlc_mutex_lock( p_mutex );
408 if(result == WAIT_TIMEOUT)
409 return ETIMEDOUT; /* this error is perfectly normal */
411 #elif defined( WIN32 )
412 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
416 mtime_t delay_ms = (deadline - mdate())/1000;
420 /* Increase our wait count */
421 p_condvar->i_waiting_threads++;
422 result = SignalObjectAndWait( *p_mutex, p_condvar->event,
424 p_condvar->i_waiting_threads--;
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 */
431 #elif defined( HAVE_KERNEL_SCHEDULER_H )
432 # error Unimplemented
439 /*****************************************************************************
440 * vlc_cond_destroy: destroy a condition
441 *****************************************************************************/
442 #define vlc_cond_destroy( P_COND ) \
443 __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
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 )
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 )
458 #if defined(LIBVLC_USE_PTHREAD)
459 i_ret = pthread_setspecific( *p_tls, p_value );
461 #elif defined( HAVE_KERNEL_SCHEDULER_H )
464 #elif defined( UNDER_CE ) || defined( WIN32 )
465 i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0;
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 )
479 #if defined(LIBVLC_USE_PTHREAD)
480 p_ret = pthread_getspecific( *p_tls );
482 #elif defined( HAVE_KERNEL_SCHEDULER_H )
485 #elif defined( UNDER_CE ) || defined( WIN32 )
486 p_ret = TlsGetValue( *p_tls );
493 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
494 typedef pthread_spinlock_t vlc_spinlock_t;
497 * Initializes a spinlock.
499 static inline int vlc_spin_init (vlc_spinlock_t *spin)
501 return pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE);
505 * Acquires a spinlock.
507 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
509 pthread_spin_lock (spin);
513 * Releases a spinlock.
515 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
517 pthread_spin_unlock (spin);
521 * Deinitializes a spinlock.
523 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
525 pthread_spin_destroy (spin);
528 #elif defined( WIN32 )
530 typedef CRITICAL_SECTION vlc_spinlock_t;
533 * Initializes a spinlock.
535 static inline int vlc_spin_init (vlc_spinlock_t *spin)
537 return !InitializeCriticalSectionAndSpinCount(spin, 4000);
541 * Acquires a spinlock.
543 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
545 EnterCriticalSection(spin);
549 * Releases a spinlock.
551 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
553 LeaveCriticalSection(spin);
557 * Deinitializes a spinlock.
559 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
561 DeleteCriticalSection(spin);
566 /* Fallback to plain mutexes if spinlocks are not available */
567 typedef vlc_mutex_t vlc_spinlock_t;
569 static inline int vlc_spin_init (vlc_spinlock_t *spin)
571 return vlc_mutex_init (spin);
574 # define vlc_spin_lock vlc_mutex_lock
575 # define vlc_spin_unlock vlc_mutex_unlock
576 # define vlc_spin_destroy vlc_mutex_destroy
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 )
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 )
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) )
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__ )
603 #endif /* !_VLC_THREADS_H */