X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=4480e41de190d365e78bae6fb87d4aacec52fb7a;hb=da9b4d4df337858b97f3841a50f7a8515d7d8f51;hp=f81f67f9a55ab22782fec838e9f35fbcf5d0a965;hpb=4c35dd214785bd12d7cf7b8489ae838cfb6cd633;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index f81f67f9a5..4480e41de1 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -1,7 +1,7 @@ /***************************************************************************** * threads.c : threads implementation for the VideoLAN client ***************************************************************************** - * Copyright (C) 1999-2007 the VideoLAN team + * Copyright (C) 1999-2008 the VideoLAN team * $Id$ * * Authors: Jean-Marc Dressler @@ -28,13 +28,15 @@ # include "config.h" #endif -#include +#include #include "libvlc.h" +#include #include #ifdef HAVE_UNISTD_H # include #endif +#include #define VLC_THREADS_UNINITIALIZED 0 #define VLC_THREADS_PENDING 1 @@ -47,6 +49,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 +66,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 +88,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 +102,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,12 +127,40 @@ 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(); +} + +static vlc_threadvar_t cancel_key; #endif +/** + * Per-thread cancellation data + */ +#ifndef LIBVLC_USE_PTHREAD_CANCEL +typedef struct vlc_cancel_t +{ + vlc_cleanup_t *cleaners; + bool killable; + bool killed; +} vlc_cancel_t; + +# define VLC_CANCEL_INIT { NULL, false, true } +#endif /***************************************************************************** * vlc_threads_init: initialize threads system @@ -136,7 +181,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,7 +190,13 @@ int vlc_threads_init( void ) } /* We should be safe now. Do all the initialization stuff we want. */ - vlc_threadvar_create( p_root, &msg_context_global_key ); +#ifndef NDEBUG + vlc_threadvar_create( &thread_object_key, NULL ); +#endif + vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy ); +#ifndef LIBVLC_USE_PTHREAD_CANCEL + vlc_threadvar_create( &cancel_key, free ); +#endif } i_initializations++; @@ -173,7 +224,16 @@ void vlc_threads_end( void ) assert( i_initializations > 0 ); if( i_initializations == 1 ) + { vlc_object_release( p_root ); +#ifndef LIBVLC_USE_PTHREAD + vlc_threadvar_delete( &cancel_key ); +#endif + vlc_threadvar_delete( &msg_context_global_key ); +#ifndef NDEBUG + vlc_threadvar_delete( &thread_object_key ); +#endif + } i_initializations--; #if defined( LIBVLC_USE_PTHREAD ) @@ -323,15 +383,12 @@ int __vlc_cond_init( vlc_cond_t *p_condvar ) return ret; #elif defined( UNDER_CE ) || defined( WIN32 ) - /* Initialize counter */ - p_condvar->i_waiting_threads = 0; - /* 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 = CreateEvent( NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* start non-signaled */ + NULL ); /* unnamed */ + return *p_condvar ? 0 : ENOMEM; #elif defined( HAVE_KERNEL_SCHEDULER_H ) if( !p_condvar ) @@ -363,7 +420,7 @@ void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condva #elif defined( UNDER_CE ) || defined( WIN32 ) VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); - CloseHandle( p_condvar->event ); + CloseHandle( *p_condvar ); #elif defined( HAVE_KERNEL_SCHEDULER_H ) p_condvar->init = 0; @@ -374,22 +431,246 @@ void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condva /***************************************************************************** * vlc_tls_create: create a thread-local variable *****************************************************************************/ -int __vlc_threadvar_create( vlc_threadvar_t *p_tls ) +int vlc_threadvar_create( vlc_threadvar_t *p_tls, void (*destr) (void *) ) { - int i_ret = -1; + int i_ret; #if defined( LIBVLC_USE_PTHREAD ) - i_ret = pthread_key_create( p_tls, NULL ); + i_ret = pthread_key_create( p_tls, destr ); #elif defined( UNDER_CE ) + i_ret = ENOSYS; #elif defined( WIN32 ) + /* FIXME: remember/use the destr() callback and stop leaking whatever */ *p_tls = TlsAlloc(); - i_ret = (*p_tls == INVALID_HANDLE_VALUE) ? EAGAIN : 0; + i_ret = (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0; #else # error Unimplemented! #endif return i_ret; } +void vlc_threadvar_delete (vlc_threadvar_t *p_tls) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_key_delete (*p_tls); +#elif defined( UNDER_CE ) +#elif defined( WIN32 ) + TlsFree (*p_tls); +#else +# error Unimplemented! +#endif +} + +#if defined (LIBVLC_USE_PTHREAD) +#elif defined (WIN32) +static unsigned __stdcall vlc_entry (void *data) +{ + vlc_cancel_t cancel_data = VLC_CANCEL_INIT; + vlc_thread_t self = data; + + vlc_threadvar_set (&cancel_key, &cancel_data); + self->data = self->entry (self->data); + return 0; +} +#endif + +/** + * Creates and starts new thread. + * + * @param p_handle [OUT] pointer to write the handle of the created thread to + * @param entry entry point for the thread + * @param data data parameter given to the entry point + * @param priority thread priority value + * @return 0 on success, a standard error code on error. + */ +int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, + int priority) +{ + int ret; + +#if defined( LIBVLC_USE_PTHREAD ) + 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 oldset; + { + sigset_t set; + 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); + } + { + struct sched_param sp = { .sched_priority = priority, }; + int policy; + + if (sp.sched_priority <= 0) + sp.sched_priority += sched_get_priority_max (policy = SCHED_OTHER); + else + sp.sched_priority += sched_get_priority_min (policy = SCHED_RR); + + pthread_attr_setschedpolicy (&attr, policy); + pthread_attr_setschedparam (&attr, &sp); + } + + ret = pthread_create (p_handle, &attr, entry, data); + 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) */ + HANDLE hThread; + vlc_thread_t th = malloc (sizeof (*p_handle)); + + if (th == NULL) + return ENOMEM; + + th->data = data; + th->entry = entry; +#if defined( UNDER_CE ) + hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL); +#else + hThread = (HANDLE)(uintptr_t) + _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL); +#endif + + if (hThread) + { + /* Thread closes the handle when exiting, duplicate it here + * to be on the safe side when joining. */ + if (!DuplicateHandle (GetCurrentProcess (), hThread, + GetCurrentProcess (), &th->handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + { + CloseHandle (hThread); + free (th); + return ENOMEM; + } + + ResumeThread (hThread); + if (priority) + SetThreadPriority (hThread, priority); + + ret = 0; + *p_handle = th; + } + else + { + ret = errno; + free (th); + } + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + *p_handle = spawn_thread( entry, psz_name, priority, data ); + ret = resume_thread( *p_handle ); + +#endif + return ret; +} + +#if defined (WIN32) +/* APC procedure for thread cancellation */ +static void CALLBACK vlc_cancel_self (ULONG_PTR dummy) +{ + (void)dummy; + vlc_control_cancel (VLC_DO_CANCEL); +} +#endif + +/** + * Marks a thread as cancelled. Next time the target thread reaches a + * cancellation point (while not having disabled cancellation), it will + * run its cancellation cleanup handler, the thread variable destructors, and + * terminate. vlc_join() must be used afterward regardless of a thread being + * cancelled or not. + */ +void vlc_cancel (vlc_thread_t thread_id) +{ +#if defined (LIBVLC_USE_PTHREAD_CANCEL) + pthread_cancel (thread_id); +#elif defined (WIN32) + QueueUserAPC (vlc_cancel_self, thread_id->handle, 0); +#else +# warning vlc_cancel is not implemented! +#endif +} + +/** + * Waits for a thread to complete (if needed), and destroys it. + * This is a cancellation point; in case of cancellation, the join does _not_ + * occur. + * + * @param handle thread handle + * @param p_result [OUT] pointer to write the thread return value or NULL + * @return 0 on success, a standard error code otherwise. + */ +void vlc_join (vlc_thread_t handle, void **result) +{ +#if defined( LIBVLC_USE_PTHREAD ) + int val = pthread_join (handle, result); + if (val) + vlc_pthread_fatal ("joining thread", val, __FILE__, __LINE__); + +#elif defined( UNDER_CE ) || defined( WIN32 ) + do + vlc_testcancel (); + while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE) + == WAIT_IO_COMPLETION); + + CloseHandle (handle->handle); + if (result) + *result = handle->data; + free (handle); + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + int32_t exit_value; + int val = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value )); + if( !val && result ) + *result = (void *)exit_value; + +#endif +} + + +struct vlc_thread_boot +{ + void * (*entry) (vlc_object_t *); + vlc_object_t *object; +}; + +static void *thread_entry (void *data) +{ + vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object; + void *(*func) (vlc_object_t *) = ((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"); + + return NULL; +} + /***************************************************************************** * vlc_thread_create: create a thread, inner version ***************************************************************************** @@ -397,112 +678,47 @@ int __vlc_threadvar_create( vlc_threadvar_t *p_tls ) * userland real-time priority threads. *****************************************************************************/ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line, - const char *psz_name, void * ( *func ) ( void * ), + const char *psz_name, void * ( *func ) ( vlc_object_t * ), 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 ); - vlc_mutex_lock( &p_this->object_lock ); + struct vlc_thread_boot *boot = malloc (sizeof (*boot)); + if (boot == NULL) + return errno; + boot->entry = func; + boot->object = p_this; -#if defined( LIBVLC_USE_PTHREAD ) - i_ret = pthread_create( &p_priv->thread_id, NULL, func, p_data ); + vlc_object_lock( p_this ); + /* Make sure we don't re-create a thread if the object has already one */ + assert( !p_priv->b_thread ); + +#if defined( LIBVLC_USE_PTHREAD ) #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) ); + /* 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; - } - 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; - } - } -#ifndef __APPLE__ - else - i_priority = 0; -#endif - -#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) */ -#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_priv->thread_id.hThread ? 0 : 1 ); - - 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_priv->thread_id = spawn_thread( (thread_func)func, psz_name, - i_priority, p_data ); - i_ret = resume_thread( p_priv->thread_id ); - #endif + i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority ); if( i_ret == 0 ) { if( b_wait ) { - msg_Dbg( p_this, "waiting for thread completion" ); + msg_Dbg( p_this, "waiting for thread initialization" ); vlc_object_wait( p_this ); } p_priv->b_thread = 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, + msg_Dbg( p_this, "thread %lu (%s) created at priority %d (%s:%d)", + (unsigned long)p_priv->thread_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 } else { @@ -511,7 +727,7 @@ 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 ); return i_ret; } @@ -524,6 +740,12 @@ 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 ) + { + msg_Err( p_this, "couldn't set priority of non-existent thread" ); + return ESRCH; + } + #if defined( LIBVLC_USE_PTHREAD ) # ifndef __APPLE__ if( config_GetInt( p_this, "rt-priority" ) > 0 ) @@ -545,10 +767,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", @@ -560,9 +780,7 @@ 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.hThread ) - p_priv->thread_id.hThread = GetCurrentThread(); - if( !SetThreadPriority(p_priv->thread_id.hThread, i_priority) ) + if( !SetThreadPriority(p_priv->thread_id->handle, i_priority) ) { msg_Warn( p_this, "couldn't set a faster priority" ); return 1; @@ -573,74 +791,36 @@ 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 *****************************************************************************/ -void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ) +void __vlc_thread_join( vlc_object_t *p_this ) { vlc_object_internals_t *p_priv = vlc_internals( p_this ); - int i_ret = 0; #if defined( LIBVLC_USE_PTHREAD ) - /* 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)) - i_ret = pthread_detach (p_priv->thread_id); - else - i_ret = pthread_join (p_priv->thread_id, NULL); + vlc_join (p_priv->thread_id, NULL); #elif defined( UNDER_CE ) || defined( WIN32 ) - HMODULE hmodule; - BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*, - FILETIME*, FILETIME* ); + HANDLE hThread; 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, + p_priv->thread_id->handle, GetCurrentProcess(), &hThread, 0, FALSE, DUPLICATE_SAME_ACCESS) ) { - msg_Err( p_this, "thread_join(%u) failed at %s:%d (%u)", - (unsigned int)p_priv->thread_id.id, - psz_file, i_line, (unsigned int)GetLastError() ); p_priv->b_thread = false; - return; + return; /* We have a problem! */ } - 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") ); + vlc_join( p_priv->thread_id, NULL ); - if( OurGetThreadTimes && - OurGetThreadTimes( hThread, - &create_ft, &exit_ft, &kernel_ft, &user_ft ) ) + if( GetThreadTimes( hThread, &create_ft, &exit_ft, &kernel_ft, &user_ft ) ) { real_time = ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) - @@ -666,21 +846,99 @@ void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line } CloseHandle( hThread ); -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - int32_t exit_value; - i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value )); +#else + vlc_join( p_priv->thread_id, NULL ); #endif - if( i_ret ) + p_priv->b_thread = false; +} + +void vlc_thread_cancel (vlc_object_t *obj) +{ + vlc_object_internals_t *priv = vlc_internals (obj); + + if (priv->b_thread) + vlc_cancel (priv->thread_id); +} + +void vlc_control_cancel (int cmd, ...) +{ + /* NOTE: This function only modifies thread-specific data, so there is no + * need to lock anything. */ +#ifdef LIBVLC_USE_PTHREAD_CANCEL + (void) cmd; + assert (0); +#else + va_list ap; + + va_start (ap, cmd); + + vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key); +#ifndef WIN32 + if (nfo == NULL) { - errno = i_ret; - msg_Err( p_this, "thread_join(%u) failed at %s:%d (%m)", - (unsigned int)p_priv->thread_id, psz_file, i_line ); + nfo = malloc (sizeof (*nfo)); + if (nfo == NULL) + abort (); + *nfo = VLC_CANCEL_INIT; + vlc_threadvar_set (&cancel_key, nfo); } - else - msg_Dbg( p_this, "thread %u joined (%s:%d)", - (unsigned int)p_priv->thread_id, psz_file, i_line ); +#endif - p_priv->b_thread = false; + switch (cmd) + { + case VLC_SAVE_CANCEL: + { + int *p_state = va_arg (ap, int *); + *p_state = nfo->killable; + nfo->killable = false; + break; + } + + case VLC_RESTORE_CANCEL: + { + int state = va_arg (ap, int); + nfo->killable = state != 0; + break; + } + + case VLC_TEST_CANCEL: + if (nfo->killable && nfo->killed) + { + for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next) + p->proc (p->data); + free (nfo); +#if defined (LIBVLC_USE_PTHREAD) + pthread_exit (PTHREAD_CANCELLED); +#elif defined (WIN32) + _endthread (); +#else +# error Not implemented! +#endif + } + break; + + case VLC_DO_CANCEL: + nfo->killed = true; + break; + + case VLC_CLEANUP_PUSH: + { + /* cleaner is a pointer to the caller stack, no need to allocate + * and copy anything. As a nice side effect, this cannot fail. */ + vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *); + cleaner->next = nfo->cleaners; + nfo->cleaners = cleaner; + break; + } + + case VLC_CLEANUP_POP: + { + nfo->cleaners = nfo->cleaners->next; + break; + } + } + va_end (ap); +#endif }