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 #if defined( WIN32 ) && !defined ETIMEDOUT
36 # define ETIMEDOUT 10060 /* This is the value in winsock.h. */
39 /*****************************************************************************
40 * Function definitions
41 *****************************************************************************/
42 VLC_EXPORT( int, __vlc_mutex_init, ( vlc_mutex_t * ) );
43 VLC_EXPORT( int, __vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
44 VLC_EXPORT( void, __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
45 VLC_EXPORT( int, __vlc_cond_init, ( vlc_cond_t * ) );
46 VLC_EXPORT( void, __vlc_cond_destroy, ( const char *, int, vlc_cond_t * ) );
47 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_threadvar_t * ) );
48 VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, vlc_bool_t ) );
49 VLC_EXPORT( int, __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
50 VLC_EXPORT( void, __vlc_thread_ready, ( vlc_object_t * ) );
51 VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, const char *, int ) );
53 /*****************************************************************************
54 * vlc_threads_init: initialize threads system
55 *****************************************************************************/
56 #define vlc_threads_init( P_THIS ) \
57 __vlc_threads_init( VLC_OBJECT(P_THIS) )
59 /*****************************************************************************
60 * vlc_threads_end: deinitialize threads system
61 *****************************************************************************/
62 #define vlc_threads_end( P_THIS ) \
63 __vlc_threads_end( VLC_OBJECT(P_THIS) )
65 /*****************************************************************************
66 * vlc_mutex_init: initialize a mutex
67 *****************************************************************************/
68 #define vlc_mutex_init( P_THIS, P_MUTEX ) \
69 __vlc_mutex_init( P_MUTEX )
71 /*****************************************************************************
72 * vlc_mutex_init: initialize a recursive mutex (Don't use it)
73 *****************************************************************************/
74 #define vlc_mutex_init_recursive( P_THIS, P_MUTEX ) \
75 __vlc_mutex_init_recursive( P_MUTEX )
77 /*****************************************************************************
78 * vlc_mutex_lock: lock a mutex
79 *****************************************************************************/
80 #define vlc_mutex_lock( P_MUTEX ) \
81 __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
83 #if defined(LIBVLC_USE_PTHREAD)
84 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
86 # define VLC_THREAD_ASSERT( action ) \
88 vlc_pthread_fatal (action, val, psz_file, i_line)
90 # define VLC_THREAD_ASSERT (void)0
93 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
94 vlc_mutex_t * p_mutex )
96 #if defined( UNDER_CE )
97 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
99 EnterCriticalSection( &p_mutex->csection );
101 #elif defined( WIN32 )
102 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
105 WaitForSingleObject( p_mutex->mutex, INFINITE );
107 EnterCriticalSection( &p_mutex->csection );
109 #elif defined( HAVE_KERNEL_SCHEDULER_H )
110 acquire_sem( p_mutex->lock );
112 #elif defined(LIBVLC_USE_PTHREAD)
113 # define vlc_assert_locked( m ) \
114 assert (pthread_mutex_lock (&((m)->mutex)) == EDEADLK)
115 int val = pthread_mutex_lock( &p_mutex->mutex );
116 VLC_THREAD_ASSERT ("locking mutex");
121 #ifndef vlc_assert_locked
122 # define vlc_assert_locked( m ) (void)0
125 /*****************************************************************************
126 * vlc_mutex_unlock: unlock a mutex
127 *****************************************************************************/
128 #define vlc_mutex_unlock( P_MUTEX ) \
129 __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
131 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
132 vlc_mutex_t *p_mutex )
134 #if defined( UNDER_CE )
135 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
137 LeaveCriticalSection( &p_mutex->csection );
139 #elif defined( WIN32 )
140 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
143 ReleaseMutex( p_mutex->mutex );
145 LeaveCriticalSection( &p_mutex->csection );
147 #elif defined( HAVE_KERNEL_SCHEDULER_H )
148 release_sem( p_mutex->lock );
150 #elif defined(LIBVLC_USE_PTHREAD)
151 int val = pthread_mutex_unlock( &p_mutex->mutex );
152 VLC_THREAD_ASSERT ("unlocking mutex");
157 /*****************************************************************************
158 * vlc_mutex_destroy: destroy a mutex
159 *****************************************************************************/
160 #define vlc_mutex_destroy( P_MUTEX ) \
161 __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
163 /*****************************************************************************
164 * vlc_cond_init: initialize a condition
165 *****************************************************************************/
166 #define vlc_cond_init( P_THIS, P_COND ) \
167 __vlc_cond_init( P_COND )
169 /*****************************************************************************
170 * vlc_cond_signal: start a thread on condition completion
171 *****************************************************************************/
172 #define vlc_cond_signal( P_COND ) \
173 __vlc_cond_signal( __FILE__, __LINE__, P_COND )
175 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
176 vlc_cond_t *p_condvar )
178 #if defined( UNDER_CE )
179 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
181 PulseEvent( p_condvar->event );
183 #elif defined( WIN32 )
184 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
186 /* Release one waiting thread if one is available. */
187 /* For this trick to work properly, the vlc_cond_signal must be surrounded
188 * by a mutex. This will prevent another thread from stealing the signal */
189 if( !p_condvar->semaphore )
191 /* PulseEvent() only works if none of the waiting threads is suspended.
192 * This is particularily problematic under a debug session.
193 * as documented in http://support.microsoft.com/kb/q173260/ */
194 PulseEvent( p_condvar->event );
196 else if( p_condvar->i_win9x_cv == 1 )
198 /* Wait for the gate to be open */
199 WaitForSingleObject( p_condvar->event, INFINITE );
201 if( p_condvar->i_waiting_threads )
203 /* Using a semaphore exposes us to a race condition. It is
204 * possible for another thread to start waiting on the semaphore
205 * just after we signaled it and thus steal the signal.
206 * We have to prevent new threads from entering the cond_wait(). */
207 ResetEvent( p_condvar->event );
209 /* A semaphore is used here because Win9x doesn't have
210 * SignalObjectAndWait() and thus a race condition exists
211 * during the time we release the mutex and the time we start
212 * waiting on the event (more precisely, the signal can sometimes
213 * be missed by the waiting thread if we use PulseEvent()). */
214 ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
219 if( p_condvar->i_waiting_threads )
221 ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
223 /* Wait for the last thread to be awakened */
224 WaitForSingleObject( p_condvar->event, INFINITE );
228 #elif defined( HAVE_KERNEL_SCHEDULER_H )
229 while( p_condvar->thread != -1 )
232 if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
235 if( info.state != B_THREAD_SUSPENDED )
237 /* The waiting thread is not suspended so it could
238 * have been interrupted beetwen the unlock and the
239 * suspend_thread line. That is why we sleep a little
240 * before retesting p_condver->thread. */
245 /* Ok, we have to wake up that thread */
246 resume_thread( p_condvar->thread );
250 #elif defined(LIBVLC_USE_PTHREAD)
251 int val = pthread_cond_signal( &p_condvar->cond );
252 VLC_THREAD_ASSERT ("signaling condition variable");
257 /*****************************************************************************
258 * vlc_cond_wait: wait until condition completion
259 *****************************************************************************/
260 #define vlc_cond_wait( P_COND, P_MUTEX ) \
261 __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX )
263 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
264 vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
266 #if defined( UNDER_CE )
267 p_condvar->i_waiting_threads++;
268 LeaveCriticalSection( &p_mutex->csection );
269 WaitForSingleObject( p_condvar->event, INFINITE );
270 p_condvar->i_waiting_threads--;
272 /* Reacquire the mutex before returning. */
273 vlc_mutex_lock( p_mutex );
275 #elif defined( WIN32 )
276 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
278 if( !p_condvar->semaphore )
280 /* Increase our wait count */
281 p_condvar->i_waiting_threads++;
285 /* It is only possible to atomically release the mutex and
286 * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
287 * SignalObjectAndWait(). */
288 p_condvar->SignalObjectAndWait( p_mutex->mutex,
294 LeaveCriticalSection( &p_mutex->csection );
295 WaitForSingleObject( p_condvar->event, INFINITE );
298 p_condvar->i_waiting_threads--;
300 else if( p_condvar->i_win9x_cv == 1 )
302 int i_waiting_threads;
304 /* Wait for the gate to be open */
305 WaitForSingleObject( p_condvar->event, INFINITE );
307 /* Increase our wait count */
308 p_condvar->i_waiting_threads++;
310 LeaveCriticalSection( &p_mutex->csection );
311 WaitForSingleObject( p_condvar->semaphore, INFINITE );
313 /* Decrement and test must be atomic */
314 EnterCriticalSection( &p_condvar->csection );
316 /* Decrease our wait count */
317 i_waiting_threads = --p_condvar->i_waiting_threads;
319 LeaveCriticalSection( &p_condvar->csection );
321 /* Reopen the gate if we were the last waiting thread */
322 if( !i_waiting_threads )
323 SetEvent( p_condvar->event );
327 int i_waiting_threads;
329 /* Increase our wait count */
330 p_condvar->i_waiting_threads++;
332 LeaveCriticalSection( &p_mutex->csection );
333 WaitForSingleObject( p_condvar->semaphore, INFINITE );
335 /* Decrement and test must be atomic */
336 EnterCriticalSection( &p_condvar->csection );
338 /* Decrease our wait count */
339 i_waiting_threads = --p_condvar->i_waiting_threads;
341 LeaveCriticalSection( &p_condvar->csection );
343 /* Signal that the last waiting thread just went through */
344 if( !i_waiting_threads )
345 SetEvent( p_condvar->event );
348 /* Reacquire the mutex before returning. */
349 vlc_mutex_lock( p_mutex );
351 #elif defined( HAVE_KERNEL_SCHEDULER_H )
352 /* The p_condvar->thread var is initialized before the unlock because
353 * it enables to identify when the thread is interrupted beetwen the
354 * unlock line and the suspend_thread line */
355 p_condvar->thread = find_thread( NULL );
356 vlc_mutex_unlock( p_mutex );
357 suspend_thread( p_condvar->thread );
358 p_condvar->thread = -1;
360 vlc_mutex_lock( p_mutex );
362 #elif defined(LIBVLC_USE_PTHREAD)
363 int val = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
364 VLC_THREAD_ASSERT ("waiting on condition");
370 /*****************************************************************************
371 * vlc_cond_timedwait: wait until condition completion or expiration
372 *****************************************************************************
373 * Returns 0 if object signaled, an error code in case of timeout or error.
374 *****************************************************************************/
375 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE ) \
376 __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE )
378 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
379 vlc_cond_t *p_condvar,
380 vlc_mutex_t *p_mutex,
383 #if defined( UNDER_CE )
384 mtime_t delay_ms = (deadline - mdate())/1000;
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--;
395 /* Reacquire the mutex before returning. */
396 vlc_mutex_lock( p_mutex );
398 if(result == WAIT_TIMEOUT)
399 return ETIMEDOUT; /* this error is perfectly normal */
401 #elif defined( WIN32 )
402 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
406 mtime_t delay_ms = (deadline - mdate())/1000;
410 if( !p_condvar->semaphore )
412 /* Increase our wait count */
413 p_condvar->i_waiting_threads++;
417 /* It is only possible to atomically release the mutex and
418 * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
419 * SignalObjectAndWait(). */
420 result = p_condvar->SignalObjectAndWait( p_mutex->mutex,
426 LeaveCriticalSection( &p_mutex->csection );
427 result = WaitForSingleObject( p_condvar->event, delay_ms );
430 p_condvar->i_waiting_threads--;
432 else if( p_condvar->i_win9x_cv == 1 )
434 int i_waiting_threads;
436 /* Wait for the gate to be open */
437 result = WaitForSingleObject( p_condvar->event, delay_ms );
439 /* Increase our wait count */
440 p_condvar->i_waiting_threads++;
442 LeaveCriticalSection( &p_mutex->csection );
445 /* recaculate remaining delay */
446 delay_ms = (deadline - mdate())/1000;
450 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
453 /* Decrement and test must be atomic */
454 EnterCriticalSection( &p_condvar->csection );
456 /* Decrease our wait count */
457 i_waiting_threads = --p_condvar->i_waiting_threads;
459 LeaveCriticalSection( &p_condvar->csection );
461 /* Reopen the gate if we were the last waiting thread */
462 if( !i_waiting_threads )
463 SetEvent( p_condvar->event );
467 int i_waiting_threads;
469 /* Increase our wait count */
470 p_condvar->i_waiting_threads++;
472 LeaveCriticalSection( &p_mutex->csection );
473 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
475 /* Decrement and test must be atomic */
476 EnterCriticalSection( &p_condvar->csection );
478 /* Decrease our wait count */
479 i_waiting_threads = --p_condvar->i_waiting_threads;
481 LeaveCriticalSection( &p_condvar->csection );
483 /* Signal that the last waiting thread just went through */
484 if( !i_waiting_threads )
485 SetEvent( p_condvar->event );
488 /* Reacquire the mutex before returning. */
489 vlc_mutex_lock( p_mutex );
490 if(result == WAIT_TIMEOUT)
491 return ETIMEDOUT; /* this error is perfectly normal */
493 #elif defined( HAVE_KERNEL_SCHEDULER_H )
494 # error Unimplemented
496 #elif defined(LIBVLC_USE_PTHREAD)
497 lldiv_t d = lldiv( deadline, 1000000 );
498 struct timespec ts = { d.quot, d.rem * 1000 };
500 int val = pthread_cond_timedwait (&p_condvar->cond, &p_mutex->mutex, &ts);
501 if (val == ETIMEDOUT)
502 return ETIMEDOUT; /* this error is perfectly normal */
503 VLC_THREAD_ASSERT ("timed-waiting on condition");
510 /*****************************************************************************
511 * vlc_cond_destroy: destroy a condition
512 *****************************************************************************/
513 #define vlc_cond_destroy( P_COND ) \
514 __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
516 /*****************************************************************************
517 * vlc_threadvar_create: create a thread-local variable
518 *****************************************************************************/
519 #define vlc_threadvar_create( PTHIS, P_TLS ) \
520 __vlc_threadvar_create( P_TLS )
522 /*****************************************************************************
523 * vlc_threadvar_set: create: set the value of a thread-local variable
524 *****************************************************************************/
525 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
529 #if defined( HAVE_KERNEL_SCHEDULER_H )
532 #elif defined( UNDER_CE ) || defined( WIN32 )
533 i_ret = ( TlsSetValue( p_tls->handle, p_value ) != 0 );
535 #elif defined(LIBVLC_USE_PTHREAD)
536 i_ret = pthread_setspecific( p_tls->handle, p_value );
543 /*****************************************************************************
544 * vlc_threadvar_get: create: get the value of a thread-local variable
545 *****************************************************************************/
546 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
550 #if defined( HAVE_KERNEL_SCHEDULER_H )
552 #elif defined( UNDER_CE ) || defined( WIN32 )
553 p_ret = TlsGetValue( p_tls->handle );
555 #elif defined(LIBVLC_USE_PTHREAD)
556 p_ret = pthread_getspecific( p_tls->handle );
563 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
566 pthread_spinlock_t spin;
570 * Initializes a spinlock.
572 static inline int vlc_spin_init (vlc_spinlock_t *spin)
574 return pthread_spin_init (&spin->spin, PTHREAD_PROCESS_PRIVATE);
578 * Acquires a spinlock.
580 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
582 int val = pthread_spin_lock (&spin->spin);
588 * Releases a spinlock.
590 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
592 int val = pthread_spin_unlock (&spin->spin);
598 * Deinitializes a spinlock.
600 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
602 int val = pthread_spin_destroy (&spin->spin);
607 #elif defined( WIN32 )
609 typedef CRITICAL_SECTION vlc_spinlock_t;
612 * Initializes a spinlock.
614 static inline int vlc_spin_init (vlc_spinlock_t *spin)
616 return !InitializeCriticalSectionAndSpinCount(spin, 4000);
620 * Acquires a spinlock.
622 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
624 EnterCriticalSection(spin);
628 * Releases a spinlock.
630 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
632 LeaveCriticalSection(spin);
636 * Deinitializes a spinlock.
638 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
640 DeleteCriticalSection(spin);
646 /* Fallback to plain mutexes if spinlocks are not available */
647 typedef vlc_mutex_t vlc_spinlock_t;
649 static inline int vlc_spin_init (vlc_spinlock_t *spin)
651 return __vlc_mutex_init (spin);
654 # define vlc_spin_lock vlc_mutex_lock
655 # define vlc_spin_unlock vlc_mutex_unlock
656 # define vlc_spin_destroy vlc_mutex_destroy
659 /*****************************************************************************
660 * vlc_thread_create: create a thread
661 *****************************************************************************/
662 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT ) \
663 __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
665 /*****************************************************************************
666 * vlc_thread_set_priority: set the priority of the calling thread
667 *****************************************************************************/
668 #define vlc_thread_set_priority( P_THIS, PRIORITY ) \
669 __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
671 /*****************************************************************************
672 * vlc_thread_ready: tell the parent thread we were successfully spawned
673 *****************************************************************************/
674 #define vlc_thread_ready( P_THIS ) \
675 __vlc_thread_ready( VLC_OBJECT(P_THIS) )
677 /*****************************************************************************
678 * vlc_thread_join: wait until a thread exits
679 *****************************************************************************/
680 #define vlc_thread_join( P_THIS ) \
681 __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )