X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmisc%2Fthreads.c;h=5a638f270c84ac753dcaf717792891b710788f29;hb=43b5fcef1b26f16914028b5954a846aff685c881;hp=4480e41de190d365e78bae6fb87d4aacec52fb7a;hpb=da9b4d4df337858b97f3841a50f7a8515d7d8f51;p=vlc diff --git a/src/misc/threads.c b/src/misc/threads.c index 4480e41de1..5a638f270c 100644 --- a/src/misc/threads.c +++ b/src/misc/threads.c @@ -8,6 +8,7 @@ * Samuel Hocevar * Gildas Bazin * Clément Sténac + * Rémi Denis-Courmont * * 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 @@ -38,11 +39,6 @@ #endif #include -#define VLC_THREADS_UNINITIALIZED 0 -#define VLC_THREADS_PENDING 1 -#define VLC_THREADS_ERROR 2 -#define VLC_THREADS_READY 3 - /***************************************************************************** * Global mutex for lazy initialization of the threads system *****************************************************************************/ @@ -52,64 +48,73 @@ static volatile unsigned i_initializations = 0; # include static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; +#else +static vlc_threadvar_t cancel_key; #endif -/** - * Global process-wide VLC object. - * Contains inter-instance data, such as the module cache and global mutexes. - */ -static libvlc_global_data_t *p_root; - -libvlc_global_data_t *vlc_global( void ) +static struct { - assert( i_initializations > 0 ); - return p_root; -} + vlc_dictionary_t list; + vlc_mutex_t lock; +} named_mutexes; + +#ifdef HAVE_EXECINFO_H +# include +#endif -#ifndef NDEBUG /** - * Object running the current thread + * Print a backtrace to the standard error for debugging purpose. */ -static vlc_threadvar_t thread_object_key; - -vlc_object_t *vlc_threadobj (void) +void vlc_trace (const char *fn, const char *file, unsigned line) { - return vlc_threadvar_get (&thread_object_key); -} + fprintf (stderr, "at %s:%u in %s\n", file, line, fn); + fflush (stderr); /* needed before switch to low-level I/O */ +#ifdef HAVE_BACKTRACE + void *stack[20]; + int len = backtrace (stack, sizeof (stack) / sizeof (stack[0])); + backtrace_symbols_fd (stack, len, 2); #endif +#ifndef WIN32 + fsync (2); +#endif +} -vlc_threadvar_t msg_context_global_key; - -#if defined(LIBVLC_USE_PTHREAD) static inline unsigned long vlc_threadid (void) { +#if defined(LIBVLC_USE_PTHREAD) union { pthread_t th; unsigned long int i; } v = { }; v.th = pthread_self (); return v.i; -} -#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) -# include +#elif defined (WIN32) + return GetCurrentThreadId (); + +#else + return 0; + #endif +} /***************************************************************************** * vlc_thread_fatal: Report an error from the threading layer ***************************************************************************** * This is mostly meant for debugging. *****************************************************************************/ -void vlc_pthread_fatal (const char *action, int error, - const char *file, unsigned line) +static void +vlc_thread_fatal (const char *action, int error, + const char *function, const char *file, unsigned line) { - fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n", - action, vlc_threadid (), file, line, error); + fprintf (stderr, "LibVLC fatal error %s (%d) in thread %lu ", + action, error, vlc_threadid ()); + vlc_trace (function, file, line); /* 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 at:\n"); -#else + fprintf (stderr, " Error message: %m\n"); +#elif !defined (WIN32) char buf[1000]; const char *msg; @@ -129,23 +134,14 @@ void vlc_pthread_fatal (const char *action, int error, #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; +#ifndef NDEBUG +# define VLC_THREAD_ASSERT( action ) \ + if (val) vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__) +#else +# define VLC_THREAD_ASSERT( action ) ((void)val) #endif /** @@ -159,7 +155,7 @@ typedef struct vlc_cancel_t bool killed; } vlc_cancel_t; -# define VLC_CANCEL_INIT { NULL, false, true } +# define VLC_CANCEL_INIT { NULL, true, false } #endif /***************************************************************************** @@ -171,8 +167,6 @@ typedef struct vlc_cancel_t *****************************************************************************/ int vlc_threads_init( void ) { - int i_ret = VLC_SUCCESS; - /* If we have lazy mutex initialization, use it. Otherwise, we just * hope nothing wrong happens. */ #if defined( LIBVLC_USE_PTHREAD ) @@ -181,33 +175,19 @@ int vlc_threads_init( void ) if( i_initializations == 0 ) { - p_root = vlc_custom_create( (vlc_object_t *)NULL, sizeof( *p_root ), - VLC_OBJECT_GENERIC, "root" ); - if( p_root == NULL ) - { - i_ret = VLC_ENOMEM; - goto out; - } - - /* 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 ); + vlc_dictionary_init (&named_mutexes.list, 0); + vlc_mutex_init (&named_mutexes.lock); #ifndef LIBVLC_USE_PTHREAD_CANCEL vlc_threadvar_create( &cancel_key, free ); #endif } i_initializations++; -out: - /* If we have lazy mutex initialization support, unlock the mutex. - * Otherwize, we are screwed. */ #if defined( LIBVLC_USE_PTHREAD ) pthread_mutex_unlock( &once_mutex ); #endif - return i_ret; + return VLC_SUCCESS; } /***************************************************************************** @@ -225,14 +205,11 @@ void vlc_threads_end( void ) 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 + vlc_mutex_destroy (&named_mutexes.lock); + vlc_dictionary_clear (&named_mutexes.list); } i_initializations--; @@ -268,34 +245,12 @@ int vlc_mutex_init( vlc_mutex_t *p_mutex ) i_result = pthread_mutex_init( p_mutex, &attr ); pthread_mutexattr_destroy( &attr ); return i_result; -#elif defined( UNDER_CE ) - InitializeCriticalSection( &p_mutex->csection ); - return 0; #elif defined( WIN32 ) - *p_mutex = CreateMutex( 0, FALSE, 0 ); - return (*p_mutex != NULL) ? 0 : ENOMEM; - -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - /* check the arguments and whether it's already been initialized */ - if( p_mutex == NULL ) - { - return B_BAD_VALUE; - } - - if( p_mutex->init == 9999 ) - { - return EALREADY; - } - - p_mutex->lock = create_sem( 1, "BeMutex" ); - if( p_mutex->lock < B_NO_ERROR ) - { - return( -1 ); - } - - p_mutex->init = 9999; - return B_OK; + /* This creates a recursive mutex. This is OK as fast mutexes have + * no defined behavior in case of recursive locking. */ + InitializeCriticalSection (&p_mutex->mutex); + return 0; #endif } @@ -318,48 +273,74 @@ int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex ) i_result = pthread_mutex_init( p_mutex, &attr ); pthread_mutexattr_destroy( &attr ); return( i_result ); + #elif defined( WIN32 ) - /* Create mutex returns a recursive mutex */ - *p_mutex = CreateMutex( 0, FALSE, 0 ); - return (*p_mutex != NULL) ? 0 : ENOMEM; -#else -# error Unimplemented! + InitializeCriticalSection( &p_mutex->mutex ); + return 0; + #endif } -/***************************************************************************** - * vlc_mutex_destroy: destroy a mutex, inner version - *****************************************************************************/ -void __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex ) +/** + * Destroys a mutex. The mutex must not be locked. + * + * @param p_mutex mutex to destroy + * @return always succeeds + */ +void vlc_mutex_destroy (vlc_mutex_t *p_mutex) { #if defined( LIBVLC_USE_PTHREAD ) int val = pthread_mutex_destroy( p_mutex ); VLC_THREAD_ASSERT ("destroying mutex"); -#elif defined( UNDER_CE ) - VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); +#elif defined( WIN32 ) + DeleteCriticalSection (&p_mutex->mutex); + +#endif +} - DeleteCriticalSection( &p_mutex->csection ); +/** + * Acquires a mutex. If needed, waits for any other thread to release it. + * Beware of deadlocks when locking multiple mutexes at the same time, + * or when using mutexes from callbacks. + * + * @param p_mutex mutex initialized with vlc_mutex_init() or + * vlc_mutex_init_recursive() + */ +void vlc_mutex_lock (vlc_mutex_t *p_mutex) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_mutex_lock( p_mutex ); + VLC_THREAD_ASSERT ("locking mutex"); #elif defined( WIN32 ) - VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + EnterCriticalSection (&p_mutex->mutex); - CloseHandle( *p_mutex ); +#endif +} -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - if( p_mutex->init == 9999 ) - delete_sem( p_mutex->lock ); - p_mutex->init = 0; +/** + * Releases a mutex (or crashes if the mutex is not locked by the caller). + * @param p_mutex mutex locked with vlc_mutex_lock(). + */ +void vlc_mutex_unlock (vlc_mutex_t *p_mutex) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_mutex_unlock( p_mutex ); + VLC_THREAD_ASSERT ("unlocking mutex"); + +#elif defined( WIN32 ) + LeaveCriticalSection (&p_mutex->mutex); #endif } /***************************************************************************** - * vlc_cond_init: initialize a condition + * vlc_cond_init: initialize a condition variable *****************************************************************************/ -int __vlc_cond_init( vlc_cond_t *p_condvar ) +int vlc_cond_init( vlc_cond_t *p_condvar ) { #if defined( LIBVLC_USE_PTHREAD ) pthread_condattr_t attr; @@ -382,48 +363,173 @@ int __vlc_cond_init( vlc_cond_t *p_condvar ) pthread_condattr_destroy (&attr); return ret; -#elif defined( UNDER_CE ) || defined( WIN32 ) - /* Create an auto-reset event. */ - *p_condvar = CreateEvent( NULL, /* no security */ - FALSE, /* auto-reset event */ - FALSE, /* start non-signaled */ - NULL ); /* unnamed */ +#elif defined( WIN32 ) + /* Create a manual-reset event (manual reset is needed for broadcast). */ + *p_condvar = CreateEvent( NULL, TRUE, FALSE, NULL ); return *p_condvar ? 0 : ENOMEM; -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - if( !p_condvar ) - { - return B_BAD_VALUE; - } +#endif +} + +/** + * Destroys a condition variable. No threads shall be waiting or signaling the + * condition. + * @param p_condvar condition variable to destroy + */ +void vlc_cond_destroy (vlc_cond_t *p_condvar) +{ +#if defined( LIBVLC_USE_PTHREAD ) + int val = pthread_cond_destroy( p_condvar ); + VLC_THREAD_ASSERT ("destroying condition"); + +#elif defined( WIN32 ) + CloseHandle( *p_condvar ); + +#endif +} + +/** + * Wakes up one thread waiting on a condition variable, if any. + * @param p_condvar condition variable + */ +void vlc_cond_signal (vlc_cond_t *p_condvar) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_cond_signal( p_condvar ); + VLC_THREAD_ASSERT ("signaling condition variable"); + +#elif defined( WIN32 ) + /* NOTE: This will cause a broadcast, that is wrong. + * This will also wake up the next waiting thread if no thread are yet + * waiting, which is also wrong. However both of these issues are allowed + * by the provision for spurious wakeups. Better have too many wakeups + * than too few (= deadlocks). */ + SetEvent (*p_condvar); + +#endif +} + +/** + * Wakes up all threads (if any) waiting on a condition variable. + * @param p_cond condition variable + */ +void vlc_cond_broadcast (vlc_cond_t *p_condvar) +{ +#if defined (LIBVLC_USE_PTHREAD) + pthread_cond_broadcast (p_condvar); + +#elif defined (WIN32) + SetEvent (*p_condvar); + +#endif +} + +#ifdef UNDER_CE +# warning FIXME +# define WaitForMultipleObjectsEx(a,b,c) WaitForMultipleObjects(a,b) +#endif + +/** + * Waits for a condition variable. The calling thread will be suspended until + * another thread calls vlc_cond_signal() or vlc_cond_broadcast() on the same + * condition variable, the thread is cancelled with vlc_cancel(), or the + * system causes a "spurious" unsolicited wake-up. + * + * A mutex is needed to wait on a condition variable. It must not be + * a recursive mutex. Although it is possible to use the same mutex for + * multiple condition, it is not valid to use different mutexes for the same + * condition variable at the same time from different threads. + * + * In case of thread cancellation, the mutex is always locked before + * cancellation proceeds. + * + * The canonical way to use a condition variable to wait for event foobar is: + @code + vlc_mutex_lock (&lock); + mutex_cleanup_push (&lock); // release the mutex in case of cancellation + + while (!foobar) + vlc_cond_wait (&wait, &lock); + + --- foobar is now true, do something about it here -- - if( p_condvar->init == 9999 ) + vlc_cleanup_run (); // release the mutex + @endcode + * + * @param p_condvar condition variable to wait on + * @param p_mutex mutex which is unlocked while waiting, + * then locked again when waking up. + * @param deadline absolute timeout + * + * @return 0 if the condition was signaled, an error code in case of timeout. + */ +void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_cond_wait( p_condvar, p_mutex ); + VLC_THREAD_ASSERT ("waiting on condition"); + +#elif defined( WIN32 ) + DWORD result; + + do { - return EALREADY; + vlc_testcancel (); + LeaveCriticalSection (&p_mutex->mutex); + result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE); + EnterCriticalSection (&p_mutex->mutex); } + while (result == WAIT_IO_COMPLETION); - p_condvar->thread = -1; - p_condvar->init = 9999; - return 0; + ResetEvent (*p_condvar); #endif } -/***************************************************************************** - * vlc_cond_destroy: destroy a condition, inner version - *****************************************************************************/ -void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar ) +/** + * Waits for a condition variable up to a certain date. + * This works like vlc_cond_wait(), except for the additional timeout. + * + * @param p_condvar condition variable to wait on + * @param p_mutex mutex which is unlocked while waiting, + * then locked again when waking up. + * @param deadline absolute timeout + * + * @return 0 if the condition was signaled, an error code in case of timeout. + */ +int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex, + mtime_t deadline) { -#if defined( LIBVLC_USE_PTHREAD ) - int val = pthread_cond_destroy( p_condvar ); - VLC_THREAD_ASSERT ("destroying condition"); +#if defined(LIBVLC_USE_PTHREAD) + lldiv_t d = lldiv( deadline, CLOCK_FREQ ); + struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) }; -#elif defined( UNDER_CE ) || defined( WIN32 ) - VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts); + if (val != ETIMEDOUT) + VLC_THREAD_ASSERT ("timed-waiting on condition"); + return val; - CloseHandle( *p_condvar ); +#elif defined( WIN32 ) + DWORD result; + + do + { + vlc_testcancel (); -#elif defined( HAVE_KERNEL_SCHEDULER_H ) - p_condvar->init = 0; + mtime_t total = (deadline - mdate ())/1000; + if( total < 0 ) + total = 0; + + DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total; + LeaveCriticalSection (&p_mutex->mutex); + result = WaitForSingleObjectEx (*p_condvar, delay, TRUE); + EnterCriticalSection (&p_mutex->mutex); + } + while (result == WAIT_IO_COMPLETION); + + ResetEvent (*p_condvar); + + return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT; #endif } @@ -527,6 +633,25 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, pthread_attr_setschedparam (&attr, &sp); } + /* The thread stack size. + * The lower the value, the less address space per thread, the highest + * maximum simultaneous threads per process. Too low values will cause + * stack overflows and weird crashes. Set with caution. Also keep in mind + * that 64-bits platforms consume more stack than 32-bits one. + * + * Thanks to on-demand paging, thread stack size only affects address space + * consumption. In terms of memory, threads only use what they need + * (rounded up to the page boundary). + * + * For example, on Linux i386, the default is 2 mega-bytes, which supports + * about 320 threads per processes. */ +#define VLC_STACKSIZE (128 * sizeof (void *) * 1024) + +#ifdef VLC_STACKSIZE + ret = pthread_attr_setstacksize (&attr, VLC_STACKSIZE); + assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */ +#endif + ret = pthread_create (p_handle, &attr, entry, data); pthread_sigmask (SIG_SETMASK, &oldset, NULL); pthread_attr_destroy (&attr); @@ -537,7 +662,7 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, * memory leaks and the signal functions not working (see Microsoft * Knowledge Base, article 104641) */ HANDLE hThread; - vlc_thread_t th = malloc (sizeof (*p_handle)); + vlc_thread_t th = malloc (sizeof (*th)); if (th == NULL) return ENOMEM; @@ -577,10 +702,6 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, 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; } @@ -626,7 +747,7 @@ 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__); + vlc_thread_fatal ("joining thread", val, __func__, __FILE__, __LINE__); #elif defined( UNDER_CE ) || defined( WIN32 ) do @@ -639,12 +760,6 @@ void vlc_join (vlc_thread_t handle, void **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 } @@ -661,9 +776,6 @@ static void *thread_entry (void *data) 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"); @@ -690,12 +802,12 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line boot->entry = func; boot->object = p_this; - 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 ) + if (b_wait) + sem_init (&p_priv->thread_ready, 0, 0); #ifndef __APPLE__ if( config_GetInt( p_this, "rt-priority" ) > 0 ) #endif @@ -704,33 +816,55 @@ int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line if( config_GetType( p_this, "rt-offset" ) ) i_priority += config_GetInt( p_this, "rt-offset" ); } +#elif defined (WIN32) + if (b_wait) + p_priv->thread_ready = CreateEvent (NULL, TRUE, FALSE, NULL); #endif + p_priv->b_thread = true; i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority ); if( i_ret == 0 ) { + 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 ); if( b_wait ) { msg_Dbg( p_this, "waiting for thread initialization" ); - vlc_object_wait( p_this ); +#if defined (LIBVLC_USE_PTHREAD) + sem_wait (&p_priv->thread_ready); + sem_destroy (&p_priv->thread_ready); +#elif defined (WIN32) + WaitForSingleObject (p_priv->thread_ready, INFINITE); + CloseHandle (p_priv->thread_ready); +#endif } - - p_priv->b_thread = true; - 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 { + p_priv->b_thread = false; errno = i_ret; msg_Err( p_this, "%s thread could not be created at %s:%d (%m)", psz_name, psz_file, i_line ); } - vlc_object_unlock( p_this ); return i_ret; } +#undef vlc_thread_ready +void vlc_thread_ready (vlc_object_t *obj) +{ + vlc_object_internals_t *priv = vlc_internals (obj); + + assert (priv->b_thread); +#if defined (LIBVLC_USE_PTHREAD) + assert (pthread_equal (pthread_self (), priv->thread_id)); + sem_post (&priv->thread_ready); +#elif defined (WIN32) + SetEvent (priv->thread_ready); +#endif +} + /***************************************************************************** * 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) @@ -872,20 +1006,22 @@ void vlc_control_cancel (int cmd, ...) #else va_list ap; - va_start (ap, cmd); - vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key); -#ifndef WIN32 if (nfo == NULL) { +#ifdef WIN32 + /* Main thread - cannot be cancelled anyway */ + return; +#else nfo = malloc (sizeof (*nfo)); if (nfo == NULL) - abort (); + return; /* Uho! Expect problems! */ *nfo = VLC_CANCEL_INIT; vlc_threadvar_set (&cancel_key, nfo); - } #endif + } + va_start (ap, cmd); switch (cmd) { case VLC_SAVE_CANCEL: @@ -908,9 +1044,13 @@ void vlc_control_cancel (int cmd, ...) { for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next) p->proc (p->data); +#ifndef WIN32 free (nfo); +#endif #if defined (LIBVLC_USE_PTHREAD) pthread_exit (PTHREAD_CANCELLED); +#elif defined (UNDER_CE) + ExitThread(0); #elif defined (WIN32) _endthread (); #else @@ -942,3 +1082,29 @@ void vlc_control_cancel (int cmd, ...) va_end (ap); #endif } + + +#undef var_AcquireMutex +/** + * Finds a process-wide mutex, creates it if needed, and locks it. + * Unlock with vlc_mutex_unlock(). + * FIXME: This is very inefficient, this is not memory-safe and this leaks + * memory. Use static locks instead. + */ +vlc_mutex_t *var_AcquireMutex( const char *name ) +{ + vlc_mutex_t *lock; + + vlc_mutex_lock (&named_mutexes.lock); + lock = vlc_dictionary_value_for_key (&named_mutexes.list, name); + if (lock == kVLCDictionaryNotFound) + { + lock = malloc (sizeof (*lock)); + vlc_mutex_init (lock); + vlc_dictionary_insert (&named_mutexes.list, name, lock); + } + vlc_mutex_unlock (&named_mutexes.lock); + + vlc_mutex_lock (lock); + return lock; +}