1 /*****************************************************************************
2 * threads_funcs.h : threads implementation for the VideoLAN client
3 * This header provides a portable threads implementation.
4 *****************************************************************************
5 * Copyright (C) 1999, 2000 VideoLAN
6 * $Id: threads_funcs.h,v 1.5 2002/06/01 12:31:58 sam Exp $
8 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9 * Samuel Hocevar <sam@zoy.org>
10 * Gildas Bazin <gbazin@netcourrier.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
28 * Function definitions
29 *****************************************************************************/
30 VLC_EXPORT( int, __vlc_threads_init, ( vlc_object_t * ) );
31 VLC_EXPORT( int, vlc_threads_end, ( void ) );
32 VLC_EXPORT( int, __vlc_mutex_init, ( vlc_object_t *, vlc_mutex_t * ) );
33 VLC_EXPORT( int, __vlc_mutex_destroy, ( char *, int, vlc_mutex_t * ) );
34 VLC_EXPORT( int, vlc_cond_init, ( vlc_cond_t * ) );
35 VLC_EXPORT( int, __vlc_cond_destroy, ( char *, int, vlc_cond_t * ) );
36 VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, char *, int, char *, void * ( * ) ( void * ), vlc_bool_t ) );
37 VLC_EXPORT( void, __vlc_thread_ready, ( vlc_object_t * ) );
38 VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, char *, int ) );
40 /*****************************************************************************
41 * vlc_threads_init: initialize threads system
42 *****************************************************************************/
43 #define vlc_threads_init( P_THIS ) \
44 __vlc_threads_init( CAST_TO_VLC_OBJECT(P_THIS) )
46 /*****************************************************************************
47 * vlc_mutex_init: initialize a mutex
48 *****************************************************************************/
49 #define vlc_mutex_init( P_THIS, P_MUTEX ) \
50 __vlc_mutex_init( CAST_TO_VLC_OBJECT(P_THIS), P_MUTEX )
52 /*****************************************************************************
53 * vlc_mutex_lock: lock a mutex
54 *****************************************************************************/
56 # define vlc_mutex_lock( P_MUTEX ) \
57 __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
59 # define vlc_mutex_lock( P_MUTEX ) \
60 __vlc_mutex_lock( "(unknown)", 0, P_MUTEX )
63 static inline int __vlc_mutex_lock( char * psz_file, int i_line,
64 vlc_mutex_t *p_mutex )
66 #if defined( PTH_INIT_IN_PTH_H )
67 return pth_mutex_acquire( p_mutex, TRUE, NULL );
69 #elif defined( ST_INIT_IN_ST_H )
70 return st_mutex_lock( *p_mutex );
72 #elif defined( WIN32 )
75 WaitForSingleObject( p_mutex->mutex, INFINITE );
79 EnterCriticalSection( &p_mutex->csection );
83 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
84 int i_return = pthread_mutex_lock( p_mutex );
87 // msg_Err( "thread %d: mutex_lock failed at %s:%d (%s)",
88 // pthread_self(), psz_file, i_line, strerror(i_return) );
92 #elif defined( HAVE_CTHREADS_H )
93 mutex_lock( p_mutex );
96 #elif defined( HAVE_KERNEL_SCHEDULER_H )
104 if( p_mutex->init < 2000 )
109 err = acquire_sem( p_mutex->lock );
115 /*****************************************************************************
116 * vlc_mutex_unlock: unlock a mutex
117 *****************************************************************************/
119 # define vlc_mutex_unlock( P_MUTEX ) \
120 __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
122 # define vlc_mutex_unlock( P_MUTEX ) \
123 __vlc_mutex_unlock( "(unknown)", 0, P_MUTEX )
126 static inline int __vlc_mutex_unlock( char * psz_file, int i_line,
127 vlc_mutex_t *p_mutex )
129 #if defined( PTH_INIT_IN_PTH_H )
130 return pth_mutex_release( p_mutex );
132 #elif defined( ST_INIT_IN_ST_H )
133 return st_mutex_unlock( *p_mutex );
135 #elif defined( WIN32 )
138 ReleaseMutex( p_mutex->mutex );
142 LeaveCriticalSection( &p_mutex->csection );
146 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
147 int i_return = pthread_mutex_unlock( p_mutex );
150 // msg_Err( "thread %d: mutex_unlock failed at %s:%d (%s)",
151 // pthread_self(), psz_file, i_line, strerror(i_return) );
155 #elif defined( HAVE_CTHREADS_H )
156 mutex_unlock( p_mutex );
159 #elif defined( HAVE_KERNEL_SCHEDULER_H )
165 if( p_mutex->init < 2000 )
170 release_sem( p_mutex->lock );
176 /*****************************************************************************
177 * vlc_mutex_destroy: destroy a mutex
178 *****************************************************************************/
180 # define vlc_mutex_destroy( P_MUTEX ) \
181 __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
183 # define vlc_mutex_destroy( P_MUTEX ) \
184 __vlc_mutex_destroy( "(unknown)", 0, P_MUTEX )
187 /*****************************************************************************
188 * vlc_cond_signal: start a thread on condition completion
189 *****************************************************************************/
190 static inline int vlc_cond_signal( vlc_cond_t *p_condvar )
192 #if defined( PTH_INIT_IN_PTH_H )
193 return pth_cond_notify( p_condvar, FALSE );
195 #elif defined( ST_INIT_IN_ST_H )
196 return st_cond_signal( *p_condvar );
198 #elif defined( WIN32 )
199 /* Release one waiting thread if one is available. */
200 /* For this trick to work properly, the vlc_cond_signal must be surrounded
201 * by a mutex. This will prevent another thread from stealing the signal */
202 PulseEvent( p_condvar->signal );
205 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
206 return pthread_cond_signal( p_condvar );
208 #elif defined( HAVE_CTHREADS_H )
209 /* condition_signal() */
210 if ( p_condvar->queue.head || p_condvar->implications )
212 cond_signal( (condition_t)p_condvar );
216 #elif defined( HAVE_KERNEL_SCHEDULER_H )
222 if( p_condvar->init < 2000 )
227 while( p_condvar->thread != -1 )
230 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 );
255 /*****************************************************************************
256 * vlc_cond_broadcast: start all threads waiting on condition completion
257 *****************************************************************************/
259 * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
260 * Only works with pthreads, you need to adapt it for others
261 * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
263 static inline int vlc_cond_broadcast( vlc_cond_t *p_condvar )
265 #if defined( PTH_INIT_IN_PTH_H )
266 return pth_cond_notify( p_condvar, FALSE );
268 #elif defined( ST_INIT_IN_ST_H )
269 return st_cond_broadcast( p_condvar );
271 #elif defined( WIN32 )
272 /* Release all waiting threads. */
273 /* For this trick to work properly, the vlc_cond_signal must be surrounded
274 * by a mutex. This will prevent another thread from stealing the signal */
275 while( p_condvar->i_waiting_threads )
277 PulseEvent( p_condvar->signal );
278 Sleep( 1 ); /* deschedule the current thread */
282 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
283 return pthread_cond_broadcast( p_condvar );
285 #elif defined( HAVE_CTHREADS_H )
286 /* condition_signal() */
287 if ( p_condvar->queue.head || p_condvar->implications )
289 cond_signal( (condition_t)p_condvar );
293 #elif defined( HAVE_KERNEL_SCHEDULER_H )
299 if( p_condvar->init < 2000 )
304 while( p_condvar->thread != -1 )
307 if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
312 if( info.state != B_THREAD_SUSPENDED )
314 /* The waiting thread is not suspended so it could
315 * have been interrupted beetwen the unlock and the
316 * suspend_thread line. That is why we sleep a little
317 * before retesting p_condver->thread. */
322 /* Ok, we have to wake up that thread */
323 resume_thread( p_condvar->thread );
332 /*****************************************************************************
333 * vlc_cond_wait: wait until condition completion
334 *****************************************************************************/
336 # define vlc_cond_wait( P_COND, P_MUTEX ) \
337 __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX )
339 # define vlc_cond_wait( P_COND, P_MUTEX ) \
340 __vlc_cond_wait( "(unknown)", 0, P_COND, P_MUTEX )
343 static inline int __vlc_cond_wait( char * psz_file, int i_line,
344 vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
346 #if defined( PTH_INIT_IN_PTH_H )
347 return pth_cond_await( p_condvar, p_mutex, NULL );
349 #elif defined( ST_INIT_IN_ST_H )
352 st_mutex_unlock( *p_mutex );
353 i_ret = st_cond_wait( *p_condvar );
354 st_mutex_lock( *p_mutex );
358 #elif defined( WIN32 )
359 /* The ideal would be to use a function which atomically releases the
360 * mutex and initiate the waiting.
361 * Unfortunately only the SignalObjectAndWait function does this and it's
362 * only supported on WinNT/2K, furthermore it cannot take multiple
363 * events as parameters.
365 * The solution we use should however fulfill all our needs (even though
366 * it is not a correct pthreads implementation)
370 p_condvar->i_waiting_threads ++;
374 p_mutex->SignalObjectAndWait( p_mutex->mutex, p_condvar->signal,
379 /* Release the mutex */
380 vlc_mutex_unlock( p_mutex );
381 i_result = WaitForSingleObject( p_condvar->signal, INFINITE);
382 p_condvar->i_waiting_threads --;
385 /* Reacquire the mutex before returning. */
386 vlc_mutex_lock( p_mutex );
388 return( i_result == WAIT_FAILED );
390 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
393 /* In debug mode, timeout */
395 struct timespec timeout;
400 gettimeofday( &now, NULL );
401 timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
402 timeout.tv_nsec = now.tv_usec * 1000;
404 i_result = pthread_cond_timedwait( p_condvar, p_mutex, &timeout );
406 if( i_result == ETIMEDOUT )
408 //X msg_Warn( "thread %d: possible deadlock detected "
409 //X "in cond_wait at %s:%d (%s)", pthread_self(),
410 //X psz_file, i_line, strerror(i_result) );
416 //X msg_Err( "thread %d: cond_wait failed at %s:%d (%s)",
417 //X pthread_self(), psz_file, i_line, strerror(i_result) );
422 return pthread_cond_wait( p_condvar, p_mutex );
425 #elif defined( HAVE_CTHREADS_H )
426 condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
429 #elif defined( HAVE_KERNEL_SCHEDULER_H )
440 if( p_condvar->init < 2000 )
445 /* The p_condvar->thread var is initialized before the unlock because
446 * it enables to identify when the thread is interrupted beetwen the
447 * unlock line and the suspend_thread line */
448 p_condvar->thread = find_thread( NULL );
449 vlc_mutex_unlock( p_mutex );
450 suspend_thread( p_condvar->thread );
451 p_condvar->thread = -1;
453 vlc_mutex_lock( p_mutex );
459 /*****************************************************************************
460 * vlc_cond_destroy: destroy a condition
461 *****************************************************************************/
463 # define vlc_cond_destroy( P_COND ) \
464 __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
466 # define vlc_cond_destroy( P_COND ) \
467 __vlc_cond_destroy( "(unknown)", 0, P_COND )
470 /*****************************************************************************
471 * vlc_thread_create: create a thread
472 *****************************************************************************/
473 # define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, WAIT ) \
474 __vlc_thread_create( CAST_TO_VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, WAIT )
476 /*****************************************************************************
477 * vlc_thread_ready: tell the parent thread we were successfully spawned
478 *****************************************************************************/
479 # define vlc_thread_ready( P_THIS ) \
480 __vlc_thread_ready( CAST_TO_VLC_OBJECT(P_THIS) )
482 /*****************************************************************************
483 * vlc_thread_join: wait until a thread exits
484 *****************************************************************************/
486 # define vlc_thread_join( P_THIS ) \
487 __vlc_thread_join( CAST_TO_VLC_OBJECT(P_THIS), __FILE__, __LINE__ )
489 # define vlc_thread_join( P_THIS ) \
490 __vlc_thread_join( CAST_TO_VLC_OBJECT(P_THIS), "(unknown)", 0 )