* threads.c : threads implementation for the VideoLAN client
*****************************************************************************
* Copyright (C) 1999, 2000, 2001, 2002 VideoLAN
- * $Id: threads.c,v 1.4 2002/06/01 18:04:49 sam Exp $
+ * $Id: threads.c,v 1.10 2002/07/20 18:01:43 sam Exp $
*
* Authors: Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
# endif /* WIN32 */
-typedef struct wrapper_s
+typedef struct wrapper_t
{
/* Data lock access */
vlc_mutex_t lock;
#endif /* GPROF */
+/*****************************************************************************
+ * Global mutexes for lazy initialization of the threads system
+ *****************************************************************************/
+static volatile int i_initializations = 0;
+
+#if defined( PTH_INIT_IN_PTH_H )
+ /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+ /* Unimplemented */
+#elif defined( WIN32 )
+ /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+ static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
+#elif defined( HAVE_CTHREADS_H )
+ /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+ /* Unimplemented */
+#endif
+
/*****************************************************************************
* vlc_threads_init: initialize threads system
+ *****************************************************************************
+ * This function requires lazy initialization of a global lock in order to
+ * keep the library really thread-safe. Some architectures don't support this
+ * and thus do not guarantee the complete reentrancy.
*****************************************************************************/
int __vlc_threads_init( vlc_object_t *p_this )
{
- /* FIXME: this is definitely _not_ threadsafe, but at least it works
- * under all implementations. We should for instance use pthread_once
- * for lazy initialization of the global lock. */
- static int i_status = VLC_THREADS_UNINITIALIZED;
- int i_ret;
+ static volatile int i_status = VLC_THREADS_UNINITIALIZED;
+ int i_ret = 0;
- if( i_status == VLC_THREADS_READY )
- {
- return 0;
- }
+#if defined( PTH_INIT_IN_PTH_H )
+ /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+ /* Unimplemented */
+#elif defined( WIN32 )
+ /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+ pthread_mutex_lock( &once_mutex );
+#elif defined( HAVE_CTHREADS_H )
+ /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+ /* Unimplemented */
+#endif
if( i_status == VLC_THREADS_UNINITIALIZED )
{
#if defined( PTH_INIT_IN_PTH_H )
i_ret = pth_init();
-
#elif defined( ST_INIT_IN_ST_H )
i_ret = st_init();
-
#elif defined( WIN32 )
- i_ret = 0;
-
+ /* Unimplemented */
#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
- i_ret = 0;
-
+ /* Unimplemented */
#elif defined( HAVE_CTHREADS_H )
- i_ret = 0;
-
+ /* Unimplemented */
#elif defined( HAVE_KERNEL_SCHEDULER_H )
- i_ret = 0;
-
+ /* Unimplemented */
#endif
+
if( i_ret )
{
i_status = VLC_THREADS_ERROR;
- return i_ret;
}
+ else
+ {
+ vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock );
+ i_status = VLC_THREADS_READY;
+ }
+ }
+ else
+ {
+ i_ret = ( i_status == VLC_THREADS_READY );
+ }
- vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock );
-
- i_status = VLC_THREADS_READY;
+ i_initializations++;
- return i_ret;
- }
+#if defined( PTH_INIT_IN_PTH_H )
+ /* Unimplemented */
+#elif defined( ST_INIT_IN_ST_H )
+ /* Unimplemented */
+#elif defined( WIN32 )
+ /* Unimplemented */
+#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+ pthread_mutex_unlock( &once_mutex );
+ return i_ret;
+#elif defined( HAVE_CTHREADS_H )
+ /* Unimplemented */
+#elif defined( HAVE_KERNEL_SCHEDULER_H )
+ /* Unimplemented */
+#endif
/* Wait until the other thread has initialized the thread library */
while( i_status == VLC_THREADS_PENDING )
msleep( THREAD_SLEEP );
}
- return( i_status == VLC_THREADS_READY );
+ return i_status == VLC_THREADS_READY;
}
/*****************************************************************************
* vlc_threads_end: stop threads system
+ *****************************************************************************
+ * FIXME: This function is far from being threadsafe. We should undo exactly
+ * what we did above in vlc_threads_init.
*****************************************************************************/
-int vlc_threads_end( void )
+int __vlc_threads_end( vlc_object_t *p_this )
{
#if defined( PTH_INIT_IN_PTH_H )
- return pth_kill();
+ i_initializations--;
+ if( i_initializations == 0 )
+ {
+ return pth_kill();
+ }
+ return 0;
#elif defined( ST_INIT_IN_ST_H )
+ i_initializations--;
return 0;
#elif defined( WIN32 )
+ i_initializations--;
return 0;
#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
+ pthread_mutex_lock( &once_mutex );
+ i_initializations--;
+ pthread_mutex_unlock( &once_mutex );
return 0;
#elif defined( HAVE_CTHREADS_H )
+ i_initializations--;
return 0;
#elif defined( HAVE_KERNEL_SCHEDULER_H )
+ i_initializations--;
return 0;
#endif
{
/* We are running on NT/2K/XP, we can use SignalObjectAndWait */
p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
- p_mutex->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait;
return ( p_mutex->mutex ? 0 : 1 );
}
else
{
- InitializeCriticalSection( &p_mutex->csection );
p_mutex->mutex = NULL;
+ InitializeCriticalSection( &p_mutex->csection );
return 0;
}
/*****************************************************************************
* vlc_cond_init: initialize a condition
*****************************************************************************/
-int vlc_cond_init( vlc_cond_t *p_condvar )
+int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar )
{
#if defined( PTH_INIT_IN_PTH_H )
return pth_cond_init( p_condvar );
return ( *p_condvar == NULL ) ? errno : 0;
#elif defined( WIN32 )
- /* initialise counter */
+ /* Initialize counter */
p_condvar->i_waiting_threads = 0;
- /* Create an auto-reset event. */
- p_condvar->signal = CreateEvent( NULL, /* no security */
- FALSE, /* auto-reset event */
- FALSE, /* non-signaled initially */
- NULL ); /* unnamed */
+ if( (GetVersion() < 0x80000000) && !p_this->p_vlc->b_fast_pthread )
+ {
+ /* Create an auto-reset event and a semaphore. */
+ p_condvar->signal = CreateEvent( NULL, FALSE, FALSE, NULL );
+ p_condvar->semaphore = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
+
+ p_condvar->b_broadcast = 0;
+
+ /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
+ p_condvar->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait;
+
+ return !p_condvar->signal || !p_condvar->semaphore;
+ }
+ else
+ {
+ p_condvar->signal = NULL;
- return( !p_condvar->signal );
+ /* Create an auto-reset event and a manual-reset event. */
+ p_condvar->p_events[0] = CreateEvent( NULL, FALSE, FALSE, NULL );
+ p_condvar->p_events[1] = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+ return !p_condvar->p_events[0] || !p_condvar->p_events[1];
+ }
#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
return pthread_cond_init( p_condvar, NULL );
return st_cond_destroy( *p_condvar );
#elif defined( WIN32 )
- return( !CloseHandle( p_condvar->signal ) );
+ if( p_condvar->signal )
+ {
+ return !CloseHandle( p_condvar->signal )
+ || !CloseHandle( p_condvar->semaphore );
+ }
+ else
+ {
+ return !CloseHandle( p_condvar->p_events[0] )
+ || !CloseHandle( p_condvar->p_events[1] );
+ }
#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
int i_result = pthread_cond_destroy( p_condvar );
{
int i_ret;
- vlc_mutex_init( p_this, &p_this->thread_lock );
- vlc_cond_init( &p_this->thread_wait );
- vlc_mutex_lock( &p_this->thread_lock );
+ vlc_mutex_lock( &p_this->object_lock );
#ifdef GPROF
wrapper_t wrapper;
wrapper.p_data = (void *)p_this;
getitimer( ITIMER_PROF, &wrapper.itimer );
vlc_mutex_init( p_this, &wrapper.lock );
- vlc_cond_init( &wrapper.wait );
+ vlc_cond_init( p_this, &wrapper.wait );
vlc_mutex_lock( &wrapper.lock );
/* Alter user-passed data so that we call the wrapper instead
{
unsigned threadID;
/* When using the MSVCRT C library you have to use the _beginthreadex
- * function instead of CreateThread, otherwise you'll end up with memory
- * leaks and the signal functions not working */
+ * function instead of CreateThread, otherwise you'll end up with
+ * memory leaks and the signal functions not working */
p_this->thread_id =
(HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func,
(void *)p_this, 0, &threadID );
}
-
+
i_ret = ( p_this->thread_id ? 0 : 1 );
#elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
if( i_ret == 0 )
{
- msg_Dbg( p_this, "thread %d (%s) created (%s:%d)",
- p_this->thread_id, psz_name, psz_file, i_line );
-
- p_this->b_thread = 1;
- p_this->i_thread = p_this->thread_id; /* We hope the cast will work */
-
if( b_wait )
{
msg_Dbg( p_this, "waiting for thread completion" );
- vlc_cond_wait( &p_this->thread_wait, &p_this->thread_lock );
+ vlc_cond_wait( &p_this->object_wait, &p_this->object_lock );
}
- vlc_mutex_unlock( &p_this->thread_lock );
+ p_this->b_thread = 1;
+
+ msg_Dbg( p_this, "thread %d (%s) created (%s:%d)",
+ p_this->thread_id, psz_name, psz_file, i_line );
+
+ vlc_mutex_unlock( &p_this->object_lock );
}
else
{
msg_Err( p_this, "%s thread could not be created at %s:%d (%s)",
psz_name, psz_file, i_line, strerror(i_ret) );
- vlc_mutex_unlock( &p_this->thread_lock );
- vlc_mutex_destroy( &p_this->thread_lock );
- vlc_cond_destroy( &p_this->thread_wait );
+ vlc_mutex_unlock( &p_this->object_lock );
}
return i_ret;
*****************************************************************************/
void __vlc_thread_ready( vlc_object_t *p_this )
{
- vlc_mutex_lock( &p_this->thread_lock );
- vlc_cond_signal( &p_this->thread_wait );
- vlc_mutex_unlock( &p_this->thread_lock );
+ vlc_mutex_lock( &p_this->object_lock );
+ vlc_cond_signal( &p_this->object_wait );
+ vlc_mutex_unlock( &p_this->object_lock );
}
/*****************************************************************************
#endif
- vlc_mutex_destroy( &p_this->thread_lock );
- vlc_cond_destroy( &p_this->thread_wait );
-
if( i_ret )
{
msg_Err( p_this, "thread_join(%d) failed at %s:%d (%s)",