X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=2405bdb3720ab654f2cbd9c8d31823c4dca07fbf;hb=54b65bedc9a2abe2461f650c0d40b387f0839174;hp=e8ae01324b64a4b1bd1aa714d987a306b8e1bfe1;hpb=fb7f7d22b070fbe51775f102cc66bd84b04e44b4;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index e8ae01324b..2405bdb372 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -28,13 +28,14 @@ # include "config.h" #endif -#include +#include #include "libvlc.h" #include #ifdef HAVE_UNISTD_H # include #endif +#include #define VLC_THREADS_UNINITIALIZED 0 #define VLC_THREADS_PENDING 1 @@ -47,6 +48,8 @@ static volatile unsigned i_initializations = 0; #if defined( LIBVLC_USE_PTHREAD ) +# include + static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; #endif @@ -62,6 +65,17 @@ libvlc_global_data_t *vlc_global( void ) return p_root; } +#ifndef NDEBUG +/** + * Object running the current thread + */ +static vlc_threadvar_t thread_object_key; + +vlc_object_t *vlc_threadobj (void) +{ + return vlc_threadvar_get (&thread_object_key); +} +#endif vlc_threadvar_t msg_context_global_key; @@ -73,6 +87,9 @@ static inline unsigned long vlc_threadid (void) return v.i; } +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) +# include +#endif /***************************************************************************** * vlc_thread_fatal: Report an error from the threading layer @@ -84,14 +101,13 @@ void vlc_pthread_fatal (const char *action, int error, { fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n", action, vlc_threadid (), file, line, error); - fflush (stderr); /* Sometimes strerror_r() crashes too, so make sure we print an error * message before we invoke it */ #ifdef __GLIBC__ /* Avoid the strerror_r() prototype brain damage in glibc */ errno = error; - fprintf (stderr, " Error message: %m\n"); + fprintf (stderr, " Error message: %m at:\n"); #else char buf[1000]; const char *msg; @@ -110,13 +126,25 @@ void vlc_pthread_fatal (const char *action, int error, } fprintf (stderr, " Error message: %s\n", msg); #endif - fflush (stderr); + +#ifdef HAVE_BACKTRACE + void *stack[20]; + int len = backtrace (stack, sizeof (stack) / sizeof (stack[0])); + backtrace_symbols_fd (stack, len, 2); +#endif + abort (); } +#else +void vlc_pthread_fatal (const char *action, int error, + const char *file, unsigned line) +{ + (void)action; (void)error; (void)file; (void)line; + abort(); +} #endif - /***************************************************************************** * vlc_threads_init: initialize threads system ***************************************************************************** @@ -136,7 +164,7 @@ int vlc_threads_init( void ) if( i_initializations == 0 ) { - p_root = vlc_custom_create( NULL, sizeof( *p_root ), + p_root = vlc_custom_create( (vlc_object_t *)NULL, sizeof( *p_root ), VLC_OBJECT_GENERIC, "root" ); if( p_root == NULL ) { @@ -145,6 +173,9 @@ int vlc_threads_init( void ) } /* We should be safe now. Do all the initialization stuff we want. */ +#ifndef NDEBUG + vlc_threadvar_create( &thread_object_key, NULL ); +#endif vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy ); } i_initializations++; @@ -176,6 +207,9 @@ void vlc_threads_end( void ) { vlc_object_release( p_root ); vlc_threadvar_delete( &msg_context_global_key ); +#ifndef NDEBUG + vlc_threadvar_delete( &thread_object_key ); +#endif } i_initializations--; @@ -397,7 +431,7 @@ int vlc_threadvar_create( vlc_threadvar_t *p_tls, void (*destr) (void *) ) void vlc_threadvar_delete (vlc_threadvar_t *p_tls) { #if defined( LIBVLC_USE_PTHREAD ) - pthread_key_delete (p_tls); + pthread_key_delete (*p_tls); #elif defined( UNDER_CE ) #elif defined( WIN32 ) TlsFree (*p_tls); @@ -406,6 +440,41 @@ void vlc_threadvar_delete (vlc_threadvar_t *p_tls) #endif } +struct vlc_thread_boot +{ + void * (*entry) (void *); + vlc_object_t *object; +}; + +#if defined (LIBVLC_USE_PTHREAD) +# define THREAD_RTYPE void * +# define THREAD_RVAL NULL +#elif defined (WIN32) +# define THREAD_RTYPE __stdcall unsigned +# define THREAD_RVAL 0 +#endif + +static THREAD_RTYPE thread_entry (void *data) +{ + vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object; + void *(*func) (void *) = ((struct vlc_thread_boot *)data)->entry; + + free (data); +#ifndef NDEBUG + vlc_threadvar_set (&thread_object_key, obj); +#endif + msg_Dbg (obj, "thread started"); + func (obj); + msg_Dbg (obj, "thread ended"); + + libvlc_priv_t *libpriv = libvlc_priv (obj->p_libvlc); + vlc_mutex_lock (&libpriv->threads_lock); + if (--libpriv->threads_count == 0) + vlc_cond_signal (&libpriv->threads_wait); + vlc_mutex_unlock (&libpriv->threads_lock); + return THREAD_RVAL; +} + /***************************************************************************** * vlc_thread_create: create a thread, inner version ***************************************************************************** @@ -417,80 +486,96 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line int i_priority, bool b_wait ) { int i_ret; - void *p_data = (void *)p_this; vlc_object_internals_t *p_priv = vlc_internals( p_this ); + libvlc_priv_t *libpriv = libvlc_priv (p_this->p_libvlc); + + struct vlc_thread_boot *boot = malloc (sizeof (*boot)); + if (boot == NULL) + return errno; + boot->entry = func; + boot->object = p_this; - vlc_mutex_lock( &p_this->object_lock ); + vlc_mutex_lock (&libpriv->threads_lock); + libpriv->threads_count++; + vlc_mutex_unlock (&libpriv->threads_lock); + + vlc_object_lock( p_this ); #if defined( LIBVLC_USE_PTHREAD ) - i_ret = pthread_create( &p_priv->thread_id, NULL, func, p_data ); + pthread_attr_t attr; + pthread_attr_init (&attr); + + /* Block the signals that signals interface plugin handles. + * If the LibVLC caller wants to handle some signals by itself, it should + * block these before whenever invoking LibVLC. And it must obviously not + * start the VLC signals interface plugin. + * + * LibVLC will normally ignore any interruption caused by an asynchronous + * signal during a system call. But there may well be some buggy cases + * where it fails to handle EINTR (bug reports welcome). Some underlying + * libraries might also not handle EINTR properly. + */ + sigset_t set, oldset; + sigemptyset (&set); + sigdelset (&set, SIGHUP); + sigaddset (&set, SIGINT); + sigaddset (&set, SIGQUIT); + sigaddset (&set, SIGTERM); + + sigaddset (&set, SIGPIPE); /* We don't want this one, really! */ + pthread_sigmask (SIG_BLOCK, &set, &oldset); #ifndef __APPLE__ if( config_GetInt( p_this, "rt-priority" ) > 0 ) #endif { - int i_error, i_policy; - struct sched_param param; + struct sched_param p = { .sched_priority = i_priority, }; + int policy; - memset( ¶m, 0, sizeof(struct sched_param) ); + /* Hack to avoid error msg */ 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; - } + p.sched_priority += config_GetInt( p_this, "rt-offset" ); + if( p.sched_priority <= 0 ) + p.sched_priority += sched_get_priority_max (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 )) ) - { - errno = i_error; - msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m", - psz_file, i_line ); - i_priority = 0; - } + p.sched_priority += sched_get_priority_min (policy = SCHED_RR); + + pthread_attr_setschedpolicy (&attr, policy); + pthread_attr_setschedparam (&attr, &p); } -#ifndef __APPLE__ - else - i_priority = 0; -#endif + + i_ret = pthread_create( &p_priv->thread_id, &attr, thread_entry, boot ); + pthread_sigmask (SIG_SETMASK, &oldset, NULL); + pthread_attr_destroy (&attr); #elif defined( WIN32 ) || defined( UNDER_CE ) - { - /* 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 (see Microsoft - * Knowledge Base, article 104641) */ + /* 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 (see Microsoft + * Knowledge Base, article 104641) */ #if defined( UNDER_CE ) - HANDLE hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)func, - (LPVOID)p_data, CREATE_SUSPENDED, - NULL ); + HANDLE hThread = CreateThread( NULL, 0, thread_entry, + (LPVOID)boot, CREATE_SUSPENDED, NULL ); #else - HANDLE hThread = (HANDLE)(uintptr_t) - _beginthreadex( NULL, 0, (LPTHREAD_START_ROUTINE)func, - (void *)p_data, CREATE_SUSPENDED, NULL ); + HANDLE hThread = (HANDLE)(uintptr_t) + _beginthreadex( NULL, 0, thread_entry, boot, CREATE_SUSPENDED, NULL ); #endif - p_priv->thread_id = hThread; - ResumeThread(hThread); - } - - i_ret = ( p_priv->thread_id ? 0 : errno ); - - if( !i_ret && i_priority ) + if( hThread ) { - if( !SetThreadPriority(p_priv->thread_id, i_priority) ) + p_priv->thread_id = hThread; + ResumeThread (hThread); + i_ret = 0; + if( i_priority && !SetThreadPriority (hThread, i_priority) ) { msg_Warn( p_this, "couldn't set a faster priority" ); i_priority = 0; } } + else + i_ret = errno; #elif defined( HAVE_KERNEL_SCHEDULER_H ) - p_priv->thread_id = spawn_thread( (thread_func)func, psz_name, + p_priv->thread_id = spawn_thread( (thread_func)thread_entry, psz_name, i_priority, p_data ); i_ret = resume_thread( p_priv->thread_id ); @@ -516,7 +601,15 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line psz_name, psz_file, i_line ); } - vlc_mutex_unlock( &p_this->object_lock ); + vlc_object_unlock( p_this ); + + if (i_ret) + { + vlc_mutex_lock (&libpriv->threads_lock); + if (--libpriv->threads_count == 0) + vlc_cond_signal (&libpriv->threads_wait); + vlc_mutex_unlock (&libpriv->threads_lock); + } return i_ret; } @@ -529,6 +622,17 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, { vlc_object_internals_t *p_priv = vlc_internals( p_this ); + if( !p_priv->b_thread ) + { +#ifndef __APPLE__ + msg_Err( p_this, "couldn't set priority of non-existent thread" ); + return ESRCH; +#else +# warning FIXME: this is wrong + p_priv->b_thread = pthread_self(); +#endif + } + #if defined( LIBVLC_USE_PTHREAD ) # ifndef __APPLE__ if( config_GetInt( p_this, "rt-priority" ) > 0 ) @@ -550,10 +654,8 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, 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 )) ) + i_policy, ¶m )) ) { errno = i_error; msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m", @@ -565,8 +667,6 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, #elif defined( WIN32 ) || defined( UNDER_CE ) VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); - if( !p_priv->thread_id ) - p_priv->thread_id = GetCurrentThread(); if( !SetThreadPriority(p_priv->thread_id, i_priority) ) { msg_Warn( p_this, "couldn't set a faster priority" ); @@ -578,14 +678,6 @@ int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, return 0; } -/***************************************************************************** - * vlc_thread_ready: tell the parent thread we were successfully spawned - *****************************************************************************/ -void __vlc_thread_ready( vlc_object_t *p_this ) -{ - vlc_object_signal( p_this ); -} - /***************************************************************************** * vlc_thread_join: wait until a thread exits, inner version *****************************************************************************/ @@ -598,7 +690,10 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line /* Make sure we do return if we are calling vlc_thread_join() * from the joined thread */ if (pthread_equal (pthread_self (), p_priv->thread_id)) + { + msg_Warn (p_this, "joining the active thread (VLC might crash)"); i_ret = pthread_detach (p_priv->thread_id); + } else i_ret = pthread_join (p_priv->thread_id, NULL);