X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=860e8ff579c9d4c7698b614fc6a827312aeaa971;hb=649f40ae919d7f0a7ac55d5780d82eff7788c4b0;hp=9a41247623d92d175fec62da31ba4ad8592050c2;hpb=4ad9a1861f0f7202c1bab7fcc14a8f2d6776a942;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index 9a41247623..860e8ff579 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -1,18 +1,19 @@ /***************************************************************************** * threads.c : threads implementation for the VideoLAN client ***************************************************************************** - * Copyright (C) 1999, 2000, 2001, 2002 VideoLAN - * $Id: threads.c,v 1.9 2002/07/16 21:29:10 sam Exp $ + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ * * Authors: Jean-Marc Dressler * Samuel Hocevar * Gildas Bazin + * Clément Sténac * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,73 +21,59 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #include +#include "libvlc.h" +#include + #define VLC_THREADS_UNINITIALIZED 0 #define VLC_THREADS_PENDING 1 #define VLC_THREADS_ERROR 2 #define VLC_THREADS_READY 3 /***************************************************************************** - * Prototype for GPROF wrapper + * Global mutex for lazy initialization of the threads system *****************************************************************************/ -#ifdef GPROF -/* Wrapper function for profiling */ -static void * vlc_thread_wrapper ( void *p_wrapper ); - -# ifdef WIN32 - -# define ITIMER_REAL 1 -# define ITIMER_PROF 2 - -struct itimerval -{ - struct timeval it_value; - struct timeval it_interval; -}; - -int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold); -# endif /* WIN32 */ - -typedef struct wrapper_s -{ - /* Data lock access */ - vlc_mutex_t lock; - vlc_cond_t wait; - - /* Data used to spawn the real thread */ - vlc_thread_func_t func; - void *p_data; - - /* Profiling timer passed to the thread */ - struct itimerval itimer; - -} wrapper_t; - -#endif /* GPROF */ - -/***************************************************************************** - * Global mutexes for lazy initialization of the threads system - *****************************************************************************/ -static volatile int i_initializations = 0; +static volatile unsigned i_initializations = 0; +static volatile int i_status = VLC_THREADS_UNINITIALIZED; +static vlc_object_t *p_root; #if defined( PTH_INIT_IN_PTH_H ) - /* Unimplemented */ #elif defined( ST_INIT_IN_ST_H ) - /* Unimplemented */ +#elif defined( UNDER_CE ) #elif defined( WIN32 ) - /* Unimplemented */ + +/* following is only available on NT/2000/XP and above */ +static SIGNALOBJECTANDWAIT pf_SignalObjectAndWait = NULL; + +/* +** On Windows NT/2K/XP we use a slow mutex implementation but which +** allows us to correctly implement condition variables. +** You can also use the faster Win9x implementation but you might +** experience problems with it. +*/ +static vlc_bool_t b_fast_mutex = VLC_FALSE; +/* +** On Windows 9x/Me you can use a fast but incorrect condition variables +** implementation (more precisely there is a possibility for a race +** condition to happen). +** However it is possible to use slower alternatives which are more robust. +** Currently you can choose between implementation 0 (which is the +** fastest but slightly incorrect), 1 (default) and 2. +*/ +static int i_win9x_cv = 1; + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) #elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; #elif defined( HAVE_CTHREADS_H ) - /* Unimplemented */ -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - /* Unimplemented */ #endif +vlc_threadvar_t msg_context_global_key; + /***************************************************************************** * vlc_threads_init: initialize threads system ***************************************************************************** @@ -96,121 +83,155 @@ static volatile int i_initializations = 0; *****************************************************************************/ int __vlc_threads_init( vlc_object_t *p_this ) { - static volatile int i_status = VLC_THREADS_UNINITIALIZED; - int i_ret = 0; + libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_this; + int i_ret = VLC_SUCCESS; + /* If we have lazy mutex initialization, use it. Otherwise, we just + * hope nothing wrong happens. */ #if defined( PTH_INIT_IN_PTH_H ) - /* Unimplemented */ #elif defined( ST_INIT_IN_ST_H ) - /* Unimplemented */ +#elif defined( UNDER_CE ) #elif defined( WIN32 ) - /* Unimplemented */ + if( IsDebuggerPresent() ) + { + /* SignalObjectAndWait() is problematic under a debugger */ + b_fast_mutex = VLC_TRUE; + i_win9x_cv = 1; + } +#elif defined( HAVE_KERNEL_SCHEDULER_H ) #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 ) { i_status = VLC_THREADS_PENDING; + /* We should be safe now. Do all the initialization stuff we want. */ + p_libvlc_global->b_ready = VLC_FALSE; + #if defined( PTH_INIT_IN_PTH_H ) - i_ret = pth_init(); + i_ret = ( pth_init() == FALSE ); + #elif defined( ST_INIT_IN_ST_H ) i_ret = st_init(); + +#elif defined( UNDER_CE ) + /* Nothing to initialize */ + #elif defined( WIN32 ) - /* Unimplemented */ + /* Dynamically get the address of SignalObjectAndWait */ + if( GetVersion() < 0x80000000 ) + { + HINSTANCE hInstLib; + + /* We are running on NT/2K/XP, we can use SignalObjectAndWait */ + hInstLib = LoadLibrary( "kernel32" ); + if( hInstLib ) + { + pf_SignalObjectAndWait = + (SIGNALOBJECTANDWAIT)GetProcAddress( hInstLib, + "SignalObjectAndWait" ); + } + } + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) #elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - /* Unimplemented */ #elif defined( HAVE_CTHREADS_H ) - /* Unimplemented */ -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - /* Unimplemented */ #endif + p_root = vlc_object_create( p_libvlc_global, VLC_OBJECT_GLOBAL ); + if( p_root == NULL ) + i_ret = VLC_ENOMEM; + if( i_ret ) { i_status = VLC_THREADS_ERROR; } else { - vlc_mutex_init( p_this, p_this->p_vlc->p_global_lock ); + i_initializations++; i_status = VLC_THREADS_READY; } + + vlc_threadvar_create( p_root, &msg_context_global_key ); } else { - i_ret = ( i_status == VLC_THREADS_READY ); + /* Just increment the initialization count */ + i_initializations++; } - i_initializations++; - + /* If we have lazy mutex initialization support, unlock the mutex; + * otherwize, do a naive wait loop. */ #if defined( PTH_INIT_IN_PTH_H ) - /* Unimplemented */ + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); #elif defined( ST_INIT_IN_ST_H ) - /* Unimplemented */ + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); +#elif defined( UNDER_CE ) + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); #elif defined( WIN32 ) - /* Unimplemented */ + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); #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 */ + while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP ); #endif - /* Wait until the other thread has initialized the thread library */ - while( i_status == VLC_THREADS_PENDING ) + if( i_status != VLC_THREADS_READY ) { - msleep( THREAD_SLEEP ); + return VLC_ETHREAD; } - return i_status == VLC_THREADS_READY; + return i_ret; } /***************************************************************************** * 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. + * FIXME: This function is far from being threadsafe. *****************************************************************************/ int __vlc_threads_end( vlc_object_t *p_this ) { + (void)p_this; #if defined( PTH_INIT_IN_PTH_H ) +#elif defined( ST_INIT_IN_ST_H ) +#elif defined( UNDER_CE ) +#elif defined( WIN32 ) +#elif defined( HAVE_KERNEL_SCHEDULER_H ) +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) + pthread_mutex_lock( &once_mutex ); +#elif defined( HAVE_CTHREADS_H ) +#endif + + if( i_initializations == 0 ) + return VLC_EGENERIC; + i_initializations--; if( i_initializations == 0 ) { - return pth_kill(); + i_status = VLC_THREADS_UNINITIALIZED; + vlc_object_destroy( p_root ); } - return 0; -#elif defined( ST_INIT_IN_ST_H ) - i_initializations--; - return 0; +#if defined( PTH_INIT_IN_PTH_H ) + if( i_initializations == 0 ) + { + return ( pth_kill() == FALSE ); + } +#elif defined( ST_INIT_IN_ST_H ) +#elif defined( UNDER_CE ) #elif defined( WIN32 ) - i_initializations--; - return 0; - +#elif defined( HAVE_KERNEL_SCHEDULER_H ) #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 + return VLC_SUCCESS; } /***************************************************************************** @@ -218,23 +239,30 @@ int __vlc_threads_end( vlc_object_t *p_this ) *****************************************************************************/ int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex ) { + assert( p_this ); + p_mutex->p_this = p_this; + #if defined( PTH_INIT_IN_PTH_H ) - return pth_mutex_init( p_mutex ); + return ( pth_mutex_init( &p_mutex->mutex ) == FALSE ); #elif defined( ST_INIT_IN_ST_H ) - *p_mutex = st_mutex_new(); - return ( *p_mutex == NULL ) ? errno : 0; + p_mutex->mutex = st_mutex_new(); + return ( p_mutex->mutex == NULL ) ? errno : 0; + +#elif defined( UNDER_CE ) + InitializeCriticalSection( &p_mutex->csection ); + return 0; #elif defined( WIN32 ) /* We use mutexes on WinNT/2K/XP because we can use the SignalObjectAndWait * function and have a 100% correct vlc_cond_wait() implementation. * As this function is not available on Win9x, we can use the faster * CriticalSections */ - if( (GetVersion() < 0x80000000) && !p_this->p_vlc->b_fast_pthread ) + if( pf_SignalObjectAndWait && !b_fast_mutex ) { /* We are running on NT/2K/XP, we can use SignalObjectAndWait */ p_mutex->mutex = CreateMutex( 0, FALSE, 0 ); - return ( p_mutex->mutex ? 0 : 1 ); + return ( p_mutex->mutex != NULL ? 0 : 1 ); } else { @@ -243,26 +271,7 @@ int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex ) return 0; } -#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) -# if defined(DEBUG) && defined(SYS_LINUX) - /* Create error-checking mutex to detect threads problems more easily. */ - pthread_mutexattr_t attr; - int i_result; - - pthread_mutexattr_init( &attr ); - pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP ); - i_result = pthread_mutex_init( p_mutex, &attr ); - pthread_mutexattr_destroy( &attr ); - return( i_result ); -# endif - return pthread_mutex_init( p_mutex, NULL ); - -#elif defined( HAVE_CTHREADS_H ) - mutex_init( p_mutex ); - return 0; - #elif defined( HAVE_KERNEL_SCHEDULER_H ) - /* check the arguments and whether it's already been initialized */ if( p_mutex == NULL ) { @@ -283,19 +292,48 @@ int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex ) p_mutex->init = 9999; return B_OK; +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) +# if defined(DEBUG) && defined(SYS_LINUX) + { + /* Create error-checking mutex to detect problems more easily. */ + pthread_mutexattr_t attr; + int i_result; + + pthread_mutexattr_init( &attr ); + pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP ); + i_result = pthread_mutex_init( &p_mutex->mutex, &attr ); + pthread_mutexattr_destroy( &attr ); + return( i_result ); + } +# endif + return pthread_mutex_init( &p_mutex->mutex, NULL ); + +#elif defined( HAVE_CTHREADS_H ) + mutex_init( p_mutex ); + return 0; + #endif } /***************************************************************************** * vlc_mutex_destroy: destroy a mutex, inner version *****************************************************************************/ -int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex ) +int __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex ) { + int i_result; + /* In case of error : */ + int i_thread = -1; + const char * psz_error = ""; + #if defined( PTH_INIT_IN_PTH_H ) return 0; #elif defined( ST_INIT_IN_ST_H ) - return st_mutex_destroy( *p_mutex ); + i_result = st_mutex_destroy( p_mutex->mutex ); + +#elif defined( UNDER_CE ) + DeleteCriticalSection( &p_mutex->csection ); + return 0; #elif defined( WIN32 ) if( p_mutex->mutex ) @@ -308,18 +346,6 @@ int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex ) } return 0; -#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - int i_return = pthread_mutex_destroy( p_mutex ); - if( i_return ) - { -//X intf_ErrMsg( "thread %d error: mutex_destroy failed at %s:%d (%s)", -//X pthread_self(), psz_file, i_line, strerror(i_return) ); - } - return i_return; - -#elif defined( HAVE_CTHREADS_H ) - return 0; - #elif defined( HAVE_KERNEL_SCHEDULER_H ) if( p_mutex->init == 9999 ) { @@ -329,7 +355,26 @@ int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex ) p_mutex->init = 0; return B_OK; -#endif +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) + i_result = pthread_mutex_destroy( &p_mutex->mutex ); + if( i_result ) + { + i_thread = CAST_PTHREAD_TO_INT(pthread_self()); + psz_error = strerror(i_result); + } + +#elif defined( HAVE_CTHREADS_H ) + return 0; + +#endif + + if( i_result ) + { + msg_Err( p_mutex->p_this, + "thread %d: mutex_destroy failed at %s:%d (%d:%s)", + i_thread, psz_file, i_line, i_result, psz_error ); + } + return i_result; } /***************************************************************************** @@ -337,53 +382,65 @@ int __vlc_mutex_destroy( char * psz_file, int i_line, vlc_mutex_t *p_mutex ) *****************************************************************************/ int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar ) { + p_condvar->p_this = p_this; + #if defined( PTH_INIT_IN_PTH_H ) - return pth_cond_init( p_condvar ); + return ( pth_cond_init( &p_condvar->cond ) == FALSE ); #elif defined( ST_INIT_IN_ST_H ) - *p_condvar = st_cond_new(); - return ( *p_condvar == NULL ) ? errno : 0; + p_condvar->cond = st_cond_new(); + return ( p_condvar->cond == NULL ) ? errno : 0; -#elif defined( WIN32 ) +#elif defined( UNDER_CE ) /* Initialize counter */ p_condvar->i_waiting_threads = 0; - 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 ); + /* Create an auto-reset event. */ + p_condvar->event = CreateEvent( NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* start non-signaled */ + NULL ); /* unnamed */ + return !p_condvar->event; - p_condvar->b_broadcast = 0; +#elif defined( WIN32 ) + /* Initialize counter */ + p_condvar->i_waiting_threads = 0; - /* We are running on NT/2K/XP, we can use SignalObjectAndWait */ - p_condvar->SignalObjectAndWait = p_this->p_vlc->SignalObjectAndWait; + /* Misc init */ + p_condvar->i_win9x_cv = i_win9x_cv; + p_condvar->SignalObjectAndWait = pf_SignalObjectAndWait; - return !p_condvar->signal || !p_condvar->semaphore; + if( (p_condvar->SignalObjectAndWait && !b_fast_mutex) + || p_condvar->i_win9x_cv == 0 ) + { + /* Create an auto-reset event. */ + p_condvar->event = CreateEvent( NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* start non-signaled */ + NULL ); /* unnamed */ + + p_condvar->semaphore = NULL; + return !p_condvar->event; } else { - p_condvar->signal = NULL; + p_condvar->semaphore = CreateSemaphore( NULL, /* no security */ + 0, /* initial count */ + 0x7fffffff, /* max count */ + NULL ); /* unnamed */ + + if( p_condvar->i_win9x_cv == 1 ) + /* Create a manual-reset event initially signaled. */ + p_condvar->event = CreateEvent( NULL, TRUE, TRUE, NULL ); + else + /* Create a auto-reset event. */ + p_condvar->event = CreateEvent( NULL, FALSE, FALSE, NULL ); - /* 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 ); + InitializeCriticalSection( &p_condvar->csection ); - return !p_condvar->p_events[0] || !p_condvar->p_events[1]; + return !p_condvar->semaphore || !p_condvar->event; } -#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - return pthread_cond_init( p_condvar, NULL ); - -#elif defined( HAVE_CTHREADS_H ) - /* condition_init() */ - spin_lock_init( &p_condvar->lock ); - cthread_queue_init( &p_condvar->queue ); - p_condvar->name = 0; - p_condvar->implications = 0; - - return 0; - #elif defined( HAVE_KERNEL_SCHEDULER_H ) if( !p_condvar ) { @@ -399,123 +456,224 @@ int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar ) p_condvar->init = 9999; return 0; +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) + pthread_condattr_t attr; + int ret; + + ret = pthread_condattr_init (&attr); + if (ret) + return ret; + +# if defined (_POSIX_CLOCK_MONOTONIC) && (_POSIX_CLOCK_MONOTONIC >= 0) + /* This must be the same clock as the one in mtime.c */ + pthread_condattr_setclock (&attr, CLOCK_MONOTONIC); +# endif + + ret = pthread_cond_init (&p_condvar->cond, &attr); + pthread_condattr_destroy (&attr); + return ret; + +#elif defined( HAVE_CTHREADS_H ) + /* condition_init() */ + spin_lock_init( &p_condvar->lock ); + cthread_queue_init( &p_condvar->queue ); + p_condvar->name = 0; + p_condvar->implications = 0; + + return 0; + #endif } /***************************************************************************** * vlc_cond_destroy: destroy a condition, inner version *****************************************************************************/ -int __vlc_cond_destroy( char * psz_file, int i_line, vlc_cond_t *p_condvar ) +int __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar ) { + int i_result; + /* In case of error : */ + int i_thread = -1; + const char * psz_error = ""; + #if defined( PTH_INIT_IN_PTH_H ) return 0; #elif defined( ST_INIT_IN_ST_H ) - return st_cond_destroy( *p_condvar ); + i_result = st_cond_destroy( p_condvar->cond ); + +#elif defined( UNDER_CE ) + i_result = !CloseHandle( p_condvar->event ); #elif defined( WIN32 ) - if( p_condvar->signal ) - { - return !CloseHandle( p_condvar->signal ) - || !CloseHandle( p_condvar->semaphore ); - } + if( !p_condvar->semaphore ) + i_result = !CloseHandle( p_condvar->event ); else - { - return !CloseHandle( p_condvar->p_events[0] ) - || !CloseHandle( p_condvar->p_events[1] ); - } + i_result = !CloseHandle( p_condvar->event ) + || !CloseHandle( p_condvar->semaphore ); + + if( p_condvar->semaphore != NULL ) + DeleteCriticalSection( &p_condvar->csection ); + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + p_condvar->init = 0; + return 0; #elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - int i_result = pthread_cond_destroy( p_condvar ); + i_result = pthread_cond_destroy( &p_condvar->cond ); if( i_result ) { -//X intf_ErrMsg( "thread %d error: cond_destroy failed at %s:%d (%s)", -//X pthread_self(), psz_file, i_line, strerror(i_result) ); + i_thread = CAST_PTHREAD_TO_INT(pthread_self()); + psz_error = strerror(i_result); } - return i_result; #elif defined( HAVE_CTHREADS_H ) return 0; +#endif + + if( i_result ) + { + msg_Err( p_condvar->p_this, + "thread %d: cond_destroy failed at %s:%d (%d:%s)", + i_thread, psz_file, i_line, i_result, psz_error ); + } + return i_result; +} + +/***************************************************************************** + * vlc_tls_create: create a thread-local variable + *****************************************************************************/ +int __vlc_threadvar_create( vlc_object_t *p_this, vlc_threadvar_t *p_tls ) +{ + int i_ret = -1; + (void)p_this; +#if defined( PTH_INIT_IN_PTH_H ) + i_ret = pth_key_create( &p_tls->handle, NULL ); #elif defined( HAVE_KERNEL_SCHEDULER_H ) - p_condvar->init = 0; - return 0; + msg_Err( p_this, "TLS not implemented" ); + i_ret VLC_EGENERIC; +#elif defined( ST_INIT_IN_ST_H ) + i_ret = st_key_create( &p_tls->handle, NULL ); +#elif defined( UNDER_CE ) || defined( WIN32 ) +#elif defined( WIN32 ) + p_tls->handle = TlsAlloc(); + i_ret = !( p_tls->handle == 0xFFFFFFFF ); -#endif +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) + i_ret = pthread_key_create( &p_tls->handle, NULL ); +#elif defined( HAVE_CTHREADS_H ) + i_ret = cthread_keycreate( &p_tls-handle ); +#endif + return i_ret; } /***************************************************************************** * vlc_thread_create: create a thread, inner version + ***************************************************************************** + * Note that i_priority is only taken into account on platforms supporting + * userland real-time priority threads. *****************************************************************************/ -int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line, - char *psz_name, void * ( *func ) ( void * ), - vlc_bool_t b_wait ) +int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line, + const char *psz_name, void * ( *func ) ( void * ), + int i_priority, vlc_bool_t b_wait ) { int i_ret; + void *p_data = (void *)p_this; + vlc_object_internals_t *p_priv = p_this->p_internals; vlc_mutex_lock( &p_this->object_lock ); -#ifdef GPROF - wrapper_t wrapper; - - /* Initialize the wrapper structure */ - wrapper.func = func; - wrapper.p_data = (void *)p_this; - getitimer( ITIMER_PROF, &wrapper.itimer ); - vlc_mutex_init( p_this, &wrapper.lock ); - vlc_cond_init( p_this, &wrapper.wait ); - vlc_mutex_lock( &wrapper.lock ); - - /* Alter user-passed data so that we call the wrapper instead - * of the real function */ - p_data = &wrapper; - func = vlc_thread_wrapper; -#endif - #if defined( PTH_INIT_IN_PTH_H ) - p_this->thread_id = pth_spawn( PTH_ATTR_DEFAULT, func, (void *)p_this ); - i_ret = 0; + p_priv->thread_id = pth_spawn( PTH_ATTR_DEFAULT, func, p_data ); + i_ret = p_priv->thread_id == NULL; #elif defined( ST_INIT_IN_ST_H ) - p_this->thread_id = st_thread_create( func, (void *)p_this, 1, 0 ); + p_priv->thread_id = st_thread_create( func, p_data, 1, 0 ); i_ret = 0; - -#elif defined( WIN32 ) + +#elif defined( WIN32 ) || defined( UNDER_CE ) { - 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 */ - p_this->thread_id = - (HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func, - (void *)p_this, 0, &threadID ); + * memory leaks and the signal functions not working (see Microsoft + * Knowledge Base, article 104641) */ +#if defined( UNDER_CE ) + DWORD threadId; + HANDLE hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)func, + (LPVOID)p_data, CREATE_SUSPENDED, + &threadId ); +#else + unsigned threadId; + uintptr_t hThread = _beginthreadex( NULL, 0, + (LPTHREAD_START_ROUTINE)func, + (void*)p_data, CREATE_SUSPENDED, + &threadId ); +#endif + p_priv->thread_id.id = (DWORD)threadId; + p_priv->thread_id.hThread = (HANDLE)hThread; + ResumeThread((HANDLE)hThread); } - i_ret = ( p_this->thread_id ? 0 : 1 ); - -#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - i_ret = pthread_create( &p_this->thread_id, NULL, func, (void *)p_this ); + i_ret = ( p_priv->thread_id.hThread ? 0 : 1 ); -#elif defined( HAVE_CTHREADS_H ) - p_this->thread_id = cthread_fork( (cthread_fn_t)func, (any_t)p_this ); - i_ret = 0; + if( i_ret && i_priority ) + { + if( !SetThreadPriority(p_priv->thread_id.hThread, i_priority) ) + { + msg_Warn( p_this, "couldn't set a faster priority" ); + i_priority = 0; + } + } #elif defined( HAVE_KERNEL_SCHEDULER_H ) - p_this->thread_id = spawn_thread( (thread_func)func, psz_name, - B_NORMAL_PRIORITY, (void *)p_this ); - i_ret = resume_thread( p_this->thread_id ); + p_priv->thread_id = spawn_thread( (thread_func)func, psz_name, + i_priority, p_data ); + i_ret = resume_thread( p_priv->thread_id ); + +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) + i_ret = pthread_create( &p_priv->thread_id, NULL, func, p_data ); +#ifndef __APPLE__ + if( config_GetInt( p_this, "rt-priority" ) ) #endif + { + int i_error, i_policy; + struct sched_param param; -#ifdef GPROF - if( i_ret == 0 ) + memset( ¶m, 0, sizeof(struct sched_param) ); + if( config_GetType( p_this, "rt-offset" ) ) + { + i_priority += config_GetInt( p_this, "rt-offset" ); + } + if( i_priority <= 0 ) + { + param.sched_priority = (-1) * i_priority; + i_policy = SCHED_OTHER; + } + else + { + param.sched_priority = i_priority; + i_policy = SCHED_RR; + } + if( (i_error = pthread_setschedparam( p_priv->thread_id, + i_policy, ¶m )) ) + { + msg_Warn( p_this, "couldn't set thread priority (%s:%d): %s", + psz_file, i_line, strerror(i_error) ); + i_priority = 0; + } + } +#ifndef __APPLE__ + else { - vlc_cond_wait( &wrapper.wait, &wrapper.lock ); + i_priority = 0; } +#endif + +#elif defined( HAVE_CTHREADS_H ) + p_priv->thread_id = cthread_fork( (cthread_fn_t)func, (any_t)p_data ); + i_ret = 0; - vlc_mutex_unlock( &wrapper.lock ); - vlc_mutex_destroy( &wrapper.lock ); - vlc_cond_destroy( &wrapper.wait ); #endif if( i_ret == 0 ) @@ -526,10 +684,18 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line, vlc_cond_wait( &p_this->object_wait, &p_this->object_lock ); } - p_this->b_thread = 1; + p_priv->b_thread = VLC_TRUE; + +#if defined( WIN32 ) || defined( UNDER_CE ) + msg_Dbg( p_this, "thread %u (%s) created at priority %d (%s:%d)", + (unsigned int)p_priv->thread_id.id, psz_name, + i_priority, psz_file, i_line ); +#else + msg_Dbg( p_this, "thread %u (%s) created at priority %d (%s:%d)", + (unsigned int)p_priv->thread_id, psz_name, i_priority, + psz_file, i_line ); +#endif - 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 ); } @@ -543,6 +709,62 @@ int __vlc_thread_create( vlc_object_t *p_this, char * psz_file, int i_line, return i_ret; } +/***************************************************************************** + * vlc_thread_set_priority: set the priority of the current thread when we + * couldn't set it in vlc_thread_create (for instance for the main thread) + *****************************************************************************/ +int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, + int i_line, int i_priority ) +{ + vlc_object_internals_t *p_priv = p_this->p_internals; +#if defined( PTH_INIT_IN_PTH_H ) || defined( ST_INIT_IN_ST_H ) +#elif defined( WIN32 ) || defined( UNDER_CE ) + if( !p_priv->thread_id.hThread ) + p_priv->thread_id.hThread = GetCurrentThread(); + if( !SetThreadPriority(p_priv->thread_id.hThread, i_priority) ) + { + msg_Warn( p_this, "couldn't set a faster priority" ); + return 1; + } + +#elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) +# ifndef __APPLE__ + if( config_GetInt( p_this, "rt-priority" ) > 0 ) +# endif + { + int i_error, i_policy; + struct sched_param param; + + memset( ¶m, 0, sizeof(struct sched_param) ); + if( config_GetType( p_this, "rt-offset" ) ) + { + i_priority += config_GetInt( p_this, "rt-offset" ); + } + if( i_priority <= 0 ) + { + param.sched_priority = (-1) * i_priority; + i_policy = SCHED_OTHER; + } + else + { + param.sched_priority = i_priority; + i_policy = SCHED_RR; + } + if( !p_priv->thread_id ) + p_priv->thread_id = pthread_self(); + if( (i_error = pthread_setschedparam( p_priv->thread_id, + i_policy, ¶m )) ) + { + msg_Warn( p_this, "couldn't set thread priority (%s:%d): %s", + psz_file, i_line, strerror(i_error) ); + i_priority = 0; + } + } +#endif + + return 0; +} + /***************************************************************************** * vlc_thread_ready: tell the parent thread we were successfully spawned *****************************************************************************/ @@ -556,65 +778,116 @@ void __vlc_thread_ready( vlc_object_t *p_this ) /***************************************************************************** * vlc_thread_join: wait until a thread exits, inner version *****************************************************************************/ -void __vlc_thread_join( vlc_object_t *p_this, char * psz_file, int i_line ) +void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ) { + vlc_object_internals_t *p_priv = p_this->p_internals; + +#if defined( UNDER_CE ) || defined( WIN32 ) + HMODULE hmodule; + BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*, + FILETIME*, FILETIME* ); + FILETIME create_ft, exit_ft, kernel_ft, user_ft; + int64_t real_time, kernel_time, user_time; + HANDLE hThread; + + /* + ** object will close its thread handle when destroyed, duplicate it here + ** to be on the safe side + */ + if( ! DuplicateHandle(GetCurrentProcess(), + p_priv->thread_id.hThread, + GetCurrentProcess(), + &hThread, + 0, + FALSE, + DUPLICATE_SAME_ACCESS) ) + { + msg_Err( p_this, "thread_join(%u) failed at %s:%d (%s)", + (unsigned int)p_priv->thread_id.id, + psz_file, i_line, GetLastError() ); + p_priv->b_thread = VLC_FALSE; + return; + } + + WaitForSingleObject( hThread, INFINITE ); + + msg_Dbg( p_this, "thread %u joined (%s:%d)", + (unsigned int)p_priv->thread_id.id, + psz_file, i_line ); +#if defined( UNDER_CE ) + hmodule = GetModuleHandle( _T("COREDLL") ); +#else + hmodule = GetModuleHandle( _T("KERNEL32") ); +#endif + OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*, + FILETIME*, FILETIME* )) + GetProcAddress( hmodule, _T("GetThreadTimes") ); + + if( OurGetThreadTimes && + OurGetThreadTimes( hThread, + &create_ft, &exit_ft, &kernel_ft, &user_ft ) ) + { + real_time = + ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) - + ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime); + real_time /= 10; + + kernel_time = + ((((int64_t)kernel_ft.dwHighDateTime)<<32)| + kernel_ft.dwLowDateTime) / 10; + + user_time = + ((((int64_t)user_ft.dwHighDateTime)<<32)| + user_ft.dwLowDateTime) / 10; + + msg_Dbg( p_this, "thread times: " + "real "I64Fd"m%fs, kernel "I64Fd"m%fs, user "I64Fd"m%fs", + real_time/60/1000000, + (double)((real_time%(60*1000000))/1000000.0), + kernel_time/60/1000000, + (double)((kernel_time%(60*1000000))/1000000.0), + user_time/60/1000000, + (double)((user_time%(60*1000000))/1000000.0) ); + } + CloseHandle( hThread ); + +#else /* !defined(WIN32) */ + int i_ret = 0; #if defined( PTH_INIT_IN_PTH_H ) - i_ret = pth_join( p_this->thread_id, NULL ); + i_ret = ( pth_join( p_priv->thread_id, NULL ) == FALSE ); #elif defined( ST_INIT_IN_ST_H ) - i_ret = st_thread_join( p_this->thread_id, NULL ); - -#elif defined( WIN32 ) - WaitForSingleObject( p_this->thread_id, INFINITE ); + i_ret = st_thread_join( p_priv->thread_id, NULL ); + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + int32_t exit_value; + i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value )); #elif defined( PTHREAD_COND_T_IN_PTHREAD_H ) - i_ret = pthread_join( p_this->thread_id, NULL ); + i_ret = pthread_join( p_priv->thread_id, NULL ); #elif defined( HAVE_CTHREADS_H ) - cthread_join( p_this->thread_id ); + cthread_join( p_priv->thread_id ); i_ret = 1; -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - int32 exit_value; - wait_for_thread( p_this->thread_id, &exit_value ); - #endif if( i_ret ) { - msg_Err( p_this, "thread_join(%d) failed at %s:%d (%s)", - p_this->thread_id, psz_file, i_line, strerror(i_ret) ); + msg_Err( p_this, "thread_join(%u) failed at %s:%d (%s)", + (unsigned int)p_priv->thread_id, psz_file, i_line, + strerror(i_ret) ); } else { - msg_Dbg( p_this, "thread %d joined (%s:%d)", - p_this->thread_id, psz_file, i_line ); + msg_Dbg( p_this, "thread %u joined (%s:%d)", + (unsigned int)p_priv->thread_id, psz_file, i_line ); } - p_this->b_thread = 0; -} - -/***************************************************************************** - * vlc_thread_wrapper: wrapper around thread functions used when profiling. - *****************************************************************************/ -#ifdef GPROF -static void *vlc_thread_wrapper( void *p_wrapper ) -{ - /* Put user data in thread-local variables */ - void * p_data = ((wrapper_t*)p_wrapper)->p_data; - vlc_thread_func_t func = ((wrapper_t*)p_wrapper)->func; - - /* Set the profile timer value */ - setitimer( ITIMER_PROF, &((wrapper_t*)p_wrapper)->itimer, NULL ); - - /* Tell the calling thread that we don't need its data anymore */ - vlc_mutex_lock( &((wrapper_t*)p_wrapper)->lock ); - vlc_cond_signal( &((wrapper_t*)p_wrapper)->wait ); - vlc_mutex_unlock( &((wrapper_t*)p_wrapper)->lock ); +#endif - /* Call the real function */ - return func( p_data ); + p_priv->b_thread = VLC_FALSE; } -#endif +