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, bool ) );
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 SignalObjectAndWait( p_mutex->mutex, p_condvar->event,
290 LeaveCriticalSection( &p_mutex->csection );
291 WaitForSingleObject( p_condvar->event, INFINITE );
294 p_condvar->i_waiting_threads--;
296 else if( p_condvar->i_win9x_cv == 1 )
298 int i_waiting_threads;
300 /* Wait for the gate to be open */
301 WaitForSingleObject( p_condvar->event, INFINITE );
303 /* Increase our wait count */
304 p_condvar->i_waiting_threads++;
306 LeaveCriticalSection( &p_mutex->csection );
307 WaitForSingleObject( p_condvar->semaphore, INFINITE );
309 /* Decrement and test must be atomic */
310 EnterCriticalSection( &p_condvar->csection );
312 /* Decrease our wait count */
313 i_waiting_threads = --p_condvar->i_waiting_threads;
315 LeaveCriticalSection( &p_condvar->csection );
317 /* Reopen the gate if we were the last waiting thread */
318 if( !i_waiting_threads )
319 SetEvent( p_condvar->event );
323 int i_waiting_threads;
325 /* Increase our wait count */
326 p_condvar->i_waiting_threads++;
328 LeaveCriticalSection( &p_mutex->csection );
329 WaitForSingleObject( p_condvar->semaphore, INFINITE );
331 /* Decrement and test must be atomic */
332 EnterCriticalSection( &p_condvar->csection );
334 /* Decrease our wait count */
335 i_waiting_threads = --p_condvar->i_waiting_threads;
337 LeaveCriticalSection( &p_condvar->csection );
339 /* Signal that the last waiting thread just went through */
340 if( !i_waiting_threads )
341 SetEvent( p_condvar->event );
344 /* Reacquire the mutex before returning. */
345 vlc_mutex_lock( p_mutex );
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;
356 vlc_mutex_lock( p_mutex );
358 #elif defined(LIBVLC_USE_PTHREAD)
359 int val = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
360 VLC_THREAD_ASSERT ("waiting on condition");
366 /*****************************************************************************
367 * vlc_cond_timedwait: wait until condition completion or expiration
368 *****************************************************************************
369 * Returns 0 if object signaled, an error code in case of timeout or error.
370 *****************************************************************************/
371 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE ) \
372 __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE )
374 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
375 vlc_cond_t *p_condvar,
376 vlc_mutex_t *p_mutex,
379 #if defined( UNDER_CE )
380 mtime_t delay_ms = (deadline - mdate())/1000;
386 p_condvar->i_waiting_threads++;
387 LeaveCriticalSection( &p_mutex->csection );
388 result = WaitForSingleObject( p_condvar->event, delay_ms );
389 p_condvar->i_waiting_threads--;
391 /* Reacquire the mutex before returning. */
392 vlc_mutex_lock( p_mutex );
394 if(result == WAIT_TIMEOUT)
395 return ETIMEDOUT; /* this error is perfectly normal */
397 #elif defined( WIN32 )
398 VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
402 mtime_t delay_ms = (deadline - mdate())/1000;
406 if( !p_condvar->semaphore )
408 /* Increase our wait count */
409 p_condvar->i_waiting_threads++;
413 result = SignalObjectAndWait( p_mutex->mutex, p_condvar->event,
418 LeaveCriticalSection( &p_mutex->csection );
419 result = WaitForSingleObject( p_condvar->event, delay_ms );
422 p_condvar->i_waiting_threads--;
424 else if( p_condvar->i_win9x_cv == 1 )
426 int i_waiting_threads;
428 /* Wait for the gate to be open */
429 result = WaitForSingleObject( p_condvar->event, delay_ms );
431 /* Increase our wait count */
432 p_condvar->i_waiting_threads++;
434 LeaveCriticalSection( &p_mutex->csection );
437 /* recaculate remaining delay */
438 delay_ms = (deadline - mdate())/1000;
442 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
445 /* Decrement and test must be atomic */
446 EnterCriticalSection( &p_condvar->csection );
448 /* Decrease our wait count */
449 i_waiting_threads = --p_condvar->i_waiting_threads;
451 LeaveCriticalSection( &p_condvar->csection );
453 /* Reopen the gate if we were the last waiting thread */
454 if( !i_waiting_threads )
455 SetEvent( p_condvar->event );
459 int i_waiting_threads;
461 /* Increase our wait count */
462 p_condvar->i_waiting_threads++;
464 LeaveCriticalSection( &p_mutex->csection );
465 result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
467 /* Decrement and test must be atomic */
468 EnterCriticalSection( &p_condvar->csection );
470 /* Decrease our wait count */
471 i_waiting_threads = --p_condvar->i_waiting_threads;
473 LeaveCriticalSection( &p_condvar->csection );
475 /* Signal that the last waiting thread just went through */
476 if( !i_waiting_threads )
477 SetEvent( p_condvar->event );
480 /* Reacquire the mutex before returning. */
481 vlc_mutex_lock( p_mutex );
482 if(result == WAIT_TIMEOUT)
483 return ETIMEDOUT; /* this error is perfectly normal */
485 #elif defined( HAVE_KERNEL_SCHEDULER_H )
486 # error Unimplemented
488 #elif defined(LIBVLC_USE_PTHREAD)
489 lldiv_t d = lldiv( deadline, 1000000 );
490 struct timespec ts = { d.quot, d.rem * 1000 };
492 int val = pthread_cond_timedwait (&p_condvar->cond, &p_mutex->mutex, &ts);
493 if (val == ETIMEDOUT)
494 return ETIMEDOUT; /* this error is perfectly normal */
495 VLC_THREAD_ASSERT ("timed-waiting on condition");
502 /*****************************************************************************
503 * vlc_cond_destroy: destroy a condition
504 *****************************************************************************/
505 #define vlc_cond_destroy( P_COND ) \
506 __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
508 /*****************************************************************************
509 * vlc_threadvar_create: create a thread-local variable
510 *****************************************************************************/
511 #define vlc_threadvar_create( PTHIS, P_TLS ) \
512 __vlc_threadvar_create( P_TLS )
514 /*****************************************************************************
515 * vlc_threadvar_set: create: set the value of a thread-local variable
516 *****************************************************************************/
517 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
521 #if defined( HAVE_KERNEL_SCHEDULER_H )
524 #elif defined( UNDER_CE ) || defined( WIN32 )
525 i_ret = ( TlsSetValue( p_tls->handle, p_value ) != 0 );
527 #elif defined(LIBVLC_USE_PTHREAD)
528 i_ret = pthread_setspecific( p_tls->handle, p_value );
535 /*****************************************************************************
536 * vlc_threadvar_get: create: get the value of a thread-local variable
537 *****************************************************************************/
538 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
542 #if defined( HAVE_KERNEL_SCHEDULER_H )
544 #elif defined( UNDER_CE ) || defined( WIN32 )
545 p_ret = TlsGetValue( p_tls->handle );
547 #elif defined(LIBVLC_USE_PTHREAD)
548 p_ret = pthread_getspecific( p_tls->handle );
555 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
558 pthread_spinlock_t spin;
562 * Initializes a spinlock.
564 static inline int vlc_spin_init (vlc_spinlock_t *spin)
566 return pthread_spin_init (&spin->spin, PTHREAD_PROCESS_PRIVATE);
570 * Acquires a spinlock.
572 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
574 int val = pthread_spin_lock (&spin->spin);
580 * Releases a spinlock.
582 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
584 int val = pthread_spin_unlock (&spin->spin);
590 * Deinitializes a spinlock.
592 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
594 int val = pthread_spin_destroy (&spin->spin);
599 #elif defined( WIN32 )
601 typedef CRITICAL_SECTION vlc_spinlock_t;
604 * Initializes a spinlock.
606 static inline int vlc_spin_init (vlc_spinlock_t *spin)
608 return !InitializeCriticalSectionAndSpinCount(spin, 4000);
612 * Acquires a spinlock.
614 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
616 EnterCriticalSection(spin);
620 * Releases a spinlock.
622 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
624 LeaveCriticalSection(spin);
628 * Deinitializes a spinlock.
630 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
632 DeleteCriticalSection(spin);
638 /* Fallback to plain mutexes if spinlocks are not available */
639 typedef vlc_mutex_t vlc_spinlock_t;
641 static inline int vlc_spin_init (vlc_spinlock_t *spin)
643 return __vlc_mutex_init (spin);
646 # define vlc_spin_lock vlc_mutex_lock
647 # define vlc_spin_unlock vlc_mutex_unlock
648 # define vlc_spin_destroy vlc_mutex_destroy
651 /*****************************************************************************
652 * vlc_thread_create: create a thread
653 *****************************************************************************/
654 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT ) \
655 __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
657 /*****************************************************************************
658 * vlc_thread_set_priority: set the priority of the calling thread
659 *****************************************************************************/
660 #define vlc_thread_set_priority( P_THIS, PRIORITY ) \
661 __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
663 /*****************************************************************************
664 * vlc_thread_ready: tell the parent thread we were successfully spawned
665 *****************************************************************************/
666 #define vlc_thread_ready( P_THIS ) \
667 __vlc_thread_ready( VLC_OBJECT(P_THIS) )
669 /*****************************************************************************
670 * vlc_thread_join: wait until a thread exits
671 *****************************************************************************/
672 #define vlc_thread_join( P_THIS ) \
673 __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )