1 /*****************************************************************************
2 * vlc_threads_funcs.h : threads implementation for the VideoLAN client
3 * This header provides a portable threads implementation.
4 *****************************************************************************
5 * Copyright (C) 1999-2007 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_THREADFUNCS_H_
33 #define _VLC_THREADFUNCS_H_
35 /*****************************************************************************
36 * Function definitions
37 *****************************************************************************/
38 VLC_EXPORT( int, __vlc_mutex_init, ( vlc_object_t *, vlc_mutex_t * ) );
39 VLC_EXPORT( int, __vlc_mutex_init_recursive, ( vlc_object_t *, vlc_mutex_t * ) );
40 VLC_EXPORT( void, __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
41 VLC_EXPORT( int, __vlc_cond_init, ( vlc_object_t *, vlc_cond_t * ) );
42 VLC_EXPORT( void, __vlc_cond_destroy, ( const char *, int, vlc_cond_t * ) );
43 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_object_t *, vlc_threadvar_t * ) );
44 VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, vlc_bool_t ) );
45 VLC_EXPORT( int, __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
46 VLC_EXPORT( void, __vlc_thread_ready, ( vlc_object_t * ) );
47 VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, const char *, int ) );
49 /*****************************************************************************
50 * vlc_threads_init: initialize threads system
51 *****************************************************************************/
52 #define vlc_threads_init( P_THIS ) \
53 __vlc_threads_init( VLC_OBJECT(P_THIS) )
55 /*****************************************************************************
56 * vlc_threads_end: deinitialize threads system
57 *****************************************************************************/
58 #define vlc_threads_end( P_THIS ) \
59 __vlc_threads_end( VLC_OBJECT(P_THIS) )
61 /*****************************************************************************
62 * vlc_mutex_init: initialize a mutex
63 *****************************************************************************/
64 #define vlc_mutex_init( P_THIS, P_MUTEX ) \
65 __vlc_mutex_init( VLC_OBJECT(P_THIS), P_MUTEX )
67 /*****************************************************************************
68 * vlc_mutex_init: initialize a recursive mutex (Don't use it)
69 *****************************************************************************/
70 #define vlc_mutex_init_recursive( P_THIS, P_MUTEX ) \
71 __vlc_mutex_init_recursive( VLC_OBJECT(P_THIS), P_MUTEX )
73 /*****************************************************************************
74 * vlc_mutex_lock: lock a mutex
75 *****************************************************************************/
76 #define vlc_mutex_lock( P_MUTEX ) \
77 __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
79 #if defined(LIBVLC_USE_PTHREAD)
80 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
82 # define VLC_THREAD_ASSERT( action ) \
84 vlc_pthread_fatal (action, val, psz_file, i_line)
86 # define VLC_THREAD_ASSERT (void)0
89 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
90 vlc_mutex_t * p_mutex )
92 #if defined( UNDER_CE )
93 EnterCriticalSection( &p_mutex->csection );
95 #elif defined( WIN32 )
97 WaitForSingleObject( p_mutex->mutex, INFINITE );
99 EnterCriticalSection( &p_mutex->csection );
101 #elif defined( HAVE_KERNEL_SCHEDULER_H )
102 acquire_sem( p_mutex->lock );
104 #elif defined(LIBVLC_USE_PTHREAD)
105 # define vlc_assert_locked( m ) \
106 assert (pthread_mutex_lock (&((m)->mutex)) == EDEADLK)
107 int val = pthread_mutex_lock( &p_mutex->mutex );
108 VLC_THREAD_ASSERT ("locking mutex");
113 #ifndef vlc_assert_locked
114 # define vlc_assert_locked( m ) (void)0
117 /*****************************************************************************
118 * vlc_mutex_unlock: unlock a mutex
119 *****************************************************************************/
120 #define vlc_mutex_unlock( P_MUTEX ) \
121 __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
123 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
124 vlc_mutex_t *p_mutex )
126 #if defined( UNDER_CE )
127 LeaveCriticalSection( &p_mutex->csection );
129 #elif defined( WIN32 )
131 ReleaseMutex( p_mutex->mutex );
133 LeaveCriticalSection( &p_mutex->csection );
135 #elif defined( HAVE_KERNEL_SCHEDULER_H )
136 release_sem( p_mutex->lock );
138 #elif defined(LIBVLC_USE_PTHREAD)
139 int val = pthread_mutex_unlock( &p_mutex->mutex );
140 VLC_THREAD_ASSERT ("unlocking mutex");
145 /*****************************************************************************
146 * vlc_mutex_destroy: destroy a mutex
147 *****************************************************************************/
148 #define vlc_mutex_destroy( P_MUTEX ) \
149 __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
151 /*****************************************************************************
152 * vlc_cond_init: initialize a condition
153 *****************************************************************************/
154 #define vlc_cond_init( P_THIS, P_COND ) \
155 __vlc_cond_init( VLC_OBJECT(P_THIS), P_COND )
157 /*****************************************************************************
158 * vlc_cond_signal: start a thread on condition completion
159 *****************************************************************************/
160 #define vlc_cond_signal( P_COND ) \
161 __vlc_cond_signal( __FILE__, __LINE__, P_COND )
163 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
164 vlc_cond_t *p_condvar )
166 #if defined( UNDER_CE )
167 PulseEvent( p_condvar->event );
169 #elif defined( WIN32 )
170 /* Release one waiting thread if one is available. */
171 /* For this trick to work properly, the vlc_cond_signal must be surrounded
172 * by a mutex. This will prevent another thread from stealing the signal */
173 if( !p_condvar->semaphore )
175 /* PulseEvent() only works if none of the waiting threads is suspended.
176 * This is particularily problematic under a debug session.
177 * as documented in http://support.microsoft.com/kb/q173260/ */
178 PulseEvent( p_condvar->event );
180 else if( p_condvar->i_win9x_cv == 1 )
182 /* Wait for the gate to be open */
183 WaitForSingleObject( p_condvar->event, INFINITE );
185 if( p_condvar->i_waiting_threads )
187 /* Using a semaphore exposes us to a race condition. It is
188 * possible for another thread to start waiting on the semaphore
189 * just after we signaled it and thus steal the signal.
190 * We have to prevent new threads from entering the cond_wait(). */
191 ResetEvent( p_condvar->event );
193 /* A semaphore is used here because Win9x doesn't have
194 * SignalObjectAndWait() and thus a race condition exists
195 * during the time we release the mutex and the time we start
196 * waiting on the event (more precisely, the signal can sometimes
197 * be missed by the waiting thread if we use PulseEvent()). */
198 ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
203 if( p_condvar->i_waiting_threads )
205 ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
207 /* Wait for the last thread to be awakened */
208 WaitForSingleObject( p_condvar->event, INFINITE );
212 #elif defined( HAVE_KERNEL_SCHEDULER_H )
213 while( p_condvar->thread != -1 )
216 if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
219 if( info.state != B_THREAD_SUSPENDED )
221 /* The waiting thread is not suspended so it could
222 * have been interrupted beetwen the unlock and the
223 * suspend_thread line. That is why we sleep a little
224 * before retesting p_condver->thread. */
229 /* Ok, we have to wake up that thread */
230 resume_thread( p_condvar->thread );
234 #elif defined(LIBVLC_USE_PTHREAD)
235 int val = pthread_cond_signal( &p_condvar->cond );
236 VLC_THREAD_ASSERT ("signaling condition variable");
241 /*****************************************************************************
242 * vlc_cond_wait: wait until condition completion
243 *****************************************************************************/
244 #define vlc_cond_wait( P_COND, P_MUTEX ) \
245 __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX )
247 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
248 vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
250 #if defined( UNDER_CE )
251 p_condvar->i_waiting_threads++;
252 LeaveCriticalSection( &p_mutex->csection );
253 WaitForSingleObject( p_condvar->event, INFINITE );
254 p_condvar->i_waiting_threads--;
256 /* Reacquire the mutex before returning. */
257 vlc_mutex_lock( p_mutex );
259 #elif defined( WIN32 )
260 if( !p_condvar->semaphore )
262 /* Increase our wait count */
263 p_condvar->i_waiting_threads++;
267 /* It is only possible to atomically release the mutex and
268 * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
269 * SignalObjectAndWait(). */
270 p_condvar->SignalObjectAndWait( p_mutex->mutex,
276 LeaveCriticalSection( &p_mutex->csection );
277 WaitForSingleObject( p_condvar->event, INFINITE );
280 p_condvar->i_waiting_threads--;
282 else if( p_condvar->i_win9x_cv == 1 )
284 int i_waiting_threads;
286 /* Wait for the gate to be open */
287 WaitForSingleObject( p_condvar->event, INFINITE );
289 /* Increase our wait count */
290 p_condvar->i_waiting_threads++;
292 LeaveCriticalSection( &p_mutex->csection );
293 WaitForSingleObject( p_condvar->semaphore, INFINITE );
295 /* Decrement and test must be atomic */
296 EnterCriticalSection( &p_condvar->csection );
298 /* Decrease our wait count */
299 i_waiting_threads = --p_condvar->i_waiting_threads;
301 LeaveCriticalSection( &p_condvar->csection );
303 /* Reopen the gate if we were the last waiting thread */
304 if( !i_waiting_threads )
305 SetEvent( p_condvar->event );
309 int i_waiting_threads;
311 /* Increase our wait count */
312 p_condvar->i_waiting_threads++;
314 LeaveCriticalSection( &p_mutex->csection );
315 WaitForSingleObject( p_condvar->semaphore, INFINITE );
317 /* Decrement and test must be atomic */
318 EnterCriticalSection( &p_condvar->csection );
320 /* Decrease our wait count */
321 i_waiting_threads = --p_condvar->i_waiting_threads;
323 LeaveCriticalSection( &p_condvar->csection );
325 /* Signal that the last waiting thread just went through */
326 if( !i_waiting_threads )
327 SetEvent( p_condvar->event );
330 /* Reacquire the mutex before returning. */
331 vlc_mutex_lock( p_mutex );
333 #elif defined( HAVE_KERNEL_SCHEDULER_H )
334 /* The p_condvar->thread var is initialized before the unlock because
335 * it enables to identify when the thread is interrupted beetwen the
336 * unlock line and the suspend_thread line */
337 p_condvar->thread = find_thread( NULL );
338 vlc_mutex_unlock( p_mutex );
339 suspend_thread( p_condvar->thread );
340 p_condvar->thread = -1;
342 vlc_mutex_lock( p_mutex );
344 #elif defined(LIBVLC_USE_PTHREAD)
345 int val = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
346 VLC_THREAD_ASSERT ("waiting on condition");
352 /*****************************************************************************
353 * vlc_cond_timedwait: wait until condition completion or expiration
354 *****************************************************************************
355 * Returns 0 if object signaled, an error code in case of timeout or error.
356 *****************************************************************************/
357 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE ) \
358 __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE )
360 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
361 vlc_cond_t *p_condvar,
362 vlc_mutex_t *p_mutex,
365 #if defined( UNDER_CE )
366 mtime_t delay_ms = (deadline - mdate())/1000;
372 p_condvar->i_waiting_threads++;
373 LeaveCriticalSection( &p_mutex->csection );
374 result = WaitForSingleObject( p_condvar->event, delay_ms );
375 p_condvar->i_waiting_threads--;
377 /* Reacquire the mutex before returning. */
378 vlc_mutex_lock( p_mutex );
380 if(result == WAIT_TIMEOUT)
381 return ETIMEDOUT; /* this error is perfectly normal */
383 #elif defined( WIN32 )
386 mtime_t delay_ms = (deadline - mdate())/1000;
390 if( !p_condvar->semaphore )
392 /* Increase our wait count */
393 p_condvar->i_waiting_threads++;
397 /* It is only possible to atomically release the mutex and
398 * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
399 * SignalObjectAndWait(). */
400 result = p_condvar->SignalObjectAndWait( p_mutex->mutex,
406 LeaveCriticalSection( &p_mutex->csection );
407 result = WaitForSingleObject( p_condvar->event, delay_ms );
410 p_condvar->i_waiting_threads--;
412 else if( p_condvar->i_win9x_cv == 1 )
414 int i_waiting_threads;
416 /* Wait for the gate to be open */
417 result = WaitForSingleObject( p_condvar->event, delay_ms );
419 /* Increase our wait count */
420 p_condvar->i_waiting_threads++;
422 LeaveCriticalSection( &p_mutex->csection );
425 /* recaculate remaining delay */
426 delay_ms = (deadline - mdate())/1000;
430 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
433 /* Decrement and test must be atomic */
434 EnterCriticalSection( &p_condvar->csection );
436 /* Decrease our wait count */
437 i_waiting_threads = --p_condvar->i_waiting_threads;
439 LeaveCriticalSection( &p_condvar->csection );
441 /* Reopen the gate if we were the last waiting thread */
442 if( !i_waiting_threads )
443 SetEvent( p_condvar->event );
447 int i_waiting_threads;
449 /* Increase our wait count */
450 p_condvar->i_waiting_threads++;
452 LeaveCriticalSection( &p_mutex->csection );
453 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
455 /* Decrement and test must be atomic */
456 EnterCriticalSection( &p_condvar->csection );
458 /* Decrease our wait count */
459 i_waiting_threads = --p_condvar->i_waiting_threads;
461 LeaveCriticalSection( &p_condvar->csection );
463 /* Signal that the last waiting thread just went through */
464 if( !i_waiting_threads )
465 SetEvent( p_condvar->event );
468 /* Reacquire the mutex before returning. */
469 vlc_mutex_lock( p_mutex );
470 if(result == WAIT_TIMEOUT)
471 return ETIMEDOUT; /* this error is perfectly normal */
473 #elif defined( HAVE_KERNEL_SCHEDULER_H )
474 # error Unimplemented
476 #elif defined(LIBVLC_USE_PTHREAD)
477 lldiv_t d = lldiv( deadline, 1000000 );
478 struct timespec ts = { d.quot, d.rem * 1000 };
480 int val = pthread_cond_timedwait (&p_condvar->cond, &p_mutex->mutex, &ts);
481 if (val == ETIMEDOUT)
482 return ETIMEDOUT; /* this error is perfectly normal */
483 VLC_THREAD_ASSERT ("timed-waiting on condition");
490 /*****************************************************************************
491 * vlc_cond_destroy: destroy a condition
492 *****************************************************************************/
493 #define vlc_cond_destroy( P_COND ) \
494 __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
496 /*****************************************************************************
497 * vlc_threadvar_create: create a thread-local variable
498 *****************************************************************************/
499 #define vlc_threadvar_create( PTHIS, P_TLS ) \
500 __vlc_threadvar_create( PTHIS, P_TLS )
502 /*****************************************************************************
503 * vlc_threadvar_set: create: set the value of a thread-local variable
504 *****************************************************************************/
505 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
509 #if defined( HAVE_KERNEL_SCHEDULER_H )
512 #elif defined( UNDER_CE ) || defined( WIN32 )
513 i_ret = ( TlsSetValue( p_tls->handle, p_value ) != 0 );
515 #elif defined(LIBVLC_USE_PTHREAD)
516 i_ret = pthread_setspecific( p_tls->handle, p_value );
523 /*****************************************************************************
524 * vlc_threadvar_get: create: get the value of a thread-local variable
525 *****************************************************************************/
526 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
530 #if defined( HAVE_KERNEL_SCHEDULER_H )
532 #elif defined( UNDER_CE ) || defined( WIN32 )
533 p_ret = TlsGetValue( p_tls->handle );
535 #elif defined(LIBVLC_USE_PTHREAD)
536 p_ret = pthread_getspecific( p_tls->handle );
543 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
546 pthread_spinlock_t spin;
550 * Initializes a spinlock.
552 static inline int vlc_spin_init (vlc_spinlock_t *spin)
554 return pthread_spin_init (&spin->spin, PTHREAD_PROCESS_PRIVATE);
558 * Acquires a spinlock.
560 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
562 int val = pthread_spin_lock (&spin->spin);
567 * Releases a spinlock.
569 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
571 int val = pthread_spin_unlock (&spin->spin);
576 * Deinitializes a spinlock.
578 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
580 int val = pthread_spin_destroy (&spin->spin);
584 #elif defined( WIN32 )
586 typedef CRITICAL_SECTION vlc_spinlock_t;
589 * Initializes a spinlock.
591 static inline int vlc_spin_init (vlc_spinlock_t *spin)
593 return !InitializeCriticalSectionAndSpinCount(spin, 4000);
597 * Acquires a spinlock.
599 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
601 EnterCriticalSection(spin);
605 * Releases a spinlock.
607 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
609 LeaveCriticalSection(spin);
613 * Deinitializes a spinlock.
615 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
617 DeleteCriticalSection(spin);
624 /* Fallback to plain mutexes if spinlocks are not available */
625 typedef vlc_mutex_t vlc_spinlock_t;
627 static inline int vlc_spin_init (vlc_spinlock_t *spin)
629 return __vlc_mutex_init (NULL, spin);
632 # define vlc_spin_lock vlc_mutex_lock
633 # define vlc_spin_unlock vlc_mutex_unlock
634 # define vlc_spin_destroy vlc_mutex_destroy
637 /*****************************************************************************
638 * vlc_thread_create: create a thread
639 *****************************************************************************/
640 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT ) \
641 __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
643 /*****************************************************************************
644 * vlc_thread_set_priority: set the priority of the calling thread
645 *****************************************************************************/
646 #define vlc_thread_set_priority( P_THIS, PRIORITY ) \
647 __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
649 /*****************************************************************************
650 * vlc_thread_ready: tell the parent thread we were successfully spawned
651 *****************************************************************************/
652 #define vlc_thread_ready( P_THIS ) \
653 __vlc_thread_ready( VLC_OBJECT(P_THIS) )
655 /*****************************************************************************
656 * vlc_thread_join: wait until a thread exits
657 *****************************************************************************/
658 #define vlc_thread_join( P_THIS ) \
659 __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )