#if defined( LIBVLC_USE_PTHREAD )
# include <sched.h>
+# ifdef __linux__
+# include <sys/syscall.h> /* SYS_gettid */
+# endif
#else
static vlc_threadvar_t cancel_key;
#endif
# include <execinfo.h>
#endif
-#ifdef UNDER_CE
-# define WaitForSingleObjectEx(a,b,c) WaitForSingleObject(a,b)
+#ifdef __APPLE__
+# include <sys/time.h> /* gettimeofday in vlc_cond_timedwait */
#endif
/**
static inline unsigned long vlc_threadid (void)
{
-#if defined(LIBVLC_USE_PTHREAD)
+#if defined (LIBVLC_USE_PTHREAD)
+# if defined (__linux__)
+ return syscall (SYS_gettid);
+
+# else
union { pthread_t th; unsigned long int i; } v = { };
v.th = pthread_self ();
return v.i;
+#endif
#elif defined (WIN32)
return GetCurrentThreadId ();
vlc_cleanup_t *cleaners;
bool killable;
bool killed;
+# ifdef UNDER_CE
+ HANDLE cancel_event;
+# endif
} vlc_cancel_t;
-# define VLC_CANCEL_INIT { NULL, true, false }
+# ifndef UNDER_CE
+# define VLC_CANCEL_INIT { NULL, true, false }
+# else
+# define VLC_CANCEL_INIT { NULL, true, false, NULL }
+# endif
+#endif
+
+#ifdef UNDER_CE
+static void CALLBACK vlc_cancel_self (ULONG_PTR dummy);
+
+static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles,
+ DWORD delay)
+{
+ vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
+ if (nfo == NULL)
+ {
+ /* Main thread - cannot be cancelled anyway */
+ return WaitForMultipleObjects (count, handles, FALSE, delay);
+ }
+ HANDLE new_handles[count + 1];
+ memcpy(new_handles, handles, count * sizeof(HANDLE));
+ new_handles[count] = nfo->cancel_event;
+ DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE,
+ delay);
+ if (result == WAIT_OBJECT_0 + count)
+ {
+ vlc_cancel_self (NULL);
+ return WAIT_IO_COMPLETION;
+ }
+ else
+ {
+ return result;
+ }
+}
+
+DWORD SleepEx (DWORD dwMilliseconds, BOOL bAlertable)
+{
+ if (bAlertable)
+ {
+ DWORD result = vlc_cancelable_wait (0, NULL, dwMilliseconds);
+ return (result == WAIT_TIMEOUT) ? 0 : WAIT_IO_COMPLETION;
+ }
+ else
+ {
+ Sleep(dwMilliseconds);
+ return 0;
+ }
+}
+
+DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ if (bAlertable)
+ {
+ /* The MSDN documentation specifies different return codes,
+ * but in practice they are the same. We just check that it
+ * remains so. */
+#if WAIT_ABANDONED != WAIT_ABANDONED_0
+# error Windows headers changed, code needs to be rewritten!
+#endif
+ return vlc_cancelable_wait (1, &hHandle, dwMilliseconds);
+ }
+ else
+ {
+ return WaitForSingleObject (hHandle, dwMilliseconds);
+ }
+}
+
+DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles,
+ BOOL bWaitAll, DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ if (bAlertable)
+ {
+ /* We do not support the bWaitAll case */
+ assert (! bWaitAll);
+ return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds);
+ }
+ else
+ {
+ return WaitForMultipleObjects (nCount, lpHandles, bWaitAll,
+ dwMilliseconds);
+ }
+}
#endif
#ifdef WIN32
VLC_THREAD_ASSERT ("destroying mutex");
#elif defined( WIN32 )
- assert (p_mutex->initialized);
+ assert (InterlockedExchange (&p_mutex->initialized, -1) == 1);
DeleteCriticalSection (&p_mutex->mutex);
#endif
* 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.
+ * This function is not a cancellation-point.
*
* @param p_mutex mutex initialized with vlc_mutex_init() or
* vlc_mutex_init_recursive()
assert (p_mutex != &super_mutex); /* this one cannot be static */
vlc_mutex_lock (&super_mutex);
- vlc_mutex_init (p_mutex);
+ if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
+ vlc_mutex_init (p_mutex);
/* FIXME: destroy the mutex some time... */
vlc_mutex_unlock (&super_mutex);
}
+ assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
EnterCriticalSection (&p_mutex->mutex);
#endif
}
+/**
+ * Acquires a mutex if and only if it is not currently held by another thread.
+ * This function never sleeps and can be used in delay-critical code paths.
+ * This function is not a cancellation-point.
+ *
+ * <b>Beware</b>: If this function fails, then the mutex is held... by another
+ * thread. The calling thread must deal with the error appropriately. That
+ * typically implies postponing the operations that would have required the
+ * mutex. If the thread cannot defer those operations, then it must use
+ * vlc_mutex_lock(). If in doubt, use vlc_mutex_lock() instead.
+ *
+ * @param p_mutex mutex initialized with vlc_mutex_init() or
+ * vlc_mutex_init_recursive()
+ * @return 0 if the mutex could be acquired, an error code otherwise.
+ */
+int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
+{
+#if defined(LIBVLC_USE_PTHREAD)
+ int val = pthread_mutex_trylock( p_mutex );
+
+ if (val != EBUSY)
+ VLC_THREAD_ASSERT ("locking mutex");
+ return val;
+
+#elif defined( WIN32 )
+ if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
+ { /* ^^ We could also lock super_mutex all the time... sluggish */
+ assert (p_mutex != &super_mutex); /* this one cannot be static */
+
+ vlc_mutex_lock (&super_mutex);
+ if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
+ vlc_mutex_init (p_mutex);
+ /* FIXME: destroy the mutex some time... */
+ vlc_mutex_unlock (&super_mutex);
+ }
+ assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
+ return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
+
+#endif
+}
/**
* Releases a mutex (or crashes if the mutex is not locked by the caller).
VLC_THREAD_ASSERT ("unlocking mutex");
#elif defined( WIN32 )
+ assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
LeaveCriticalSection (&p_mutex->mutex);
#endif
#endif
}
-#ifdef UNDER_CE
-# 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
mtime_t deadline)
{
#if defined(LIBVLC_USE_PTHREAD)
+#ifdef __APPLE__
+ /* mdate() is mac_absolute_time on osx, which we must convert to do
+ * the same base than gettimeofday() on which pthread_cond_timedwait
+ * counts on. */
+ mtime_t oldbase = mdate();
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ mtime_t newbase = (mtime_t)tv.tv_sec * 1000000 + (mtime_t) tv.tv_usec;
+ deadline = deadline - oldbase + newbase;
+#endif
lldiv_t d = lldiv( deadline, CLOCK_FREQ );
struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
#endif
}
+/**
+ * Sets a thread-local variable.
+ * @param key thread-local variable key (created with vlc_threadvar_create())
+ * @param value new value for the variable for the calling thread
+ * @return 0 on success, a system error code otherwise.
+ */
+int vlc_threadvar_set (vlc_threadvar_t key, void *value)
+{
+#if defined(LIBVLC_USE_PTHREAD)
+ return pthread_setspecific (key, value);
+#elif defined( UNDER_CE ) || defined( WIN32 )
+ return TlsSetValue (key, p_value) ? ENOMEM : 0;
+#else
+# error Unimplemented!
+#endif
+}
+
+/**
+ * Gets the value of a thread-local variable for the calling thread.
+ * This function cannot fail.
+ * @return the value associated with the given variable for the calling
+ * or NULL if there is no value.
+ */
+void *vlc_threadvar_get (vlc_threadvar_t key)
+{
+#if defined(LIBVLC_USE_PTHREAD)
+ return pthread_getspecific (key);
+#elif defined( UNDER_CE ) || defined( WIN32 )
+ return TlsGetValue (key);
+#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;
+#ifdef UNDER_CE
+ cancel_data.cancel_event = self->cancel_event;
+#endif
- vlc_threadvar_set (&cancel_key, &cancel_data);
+ vlc_threadvar_set (cancel_key, &cancel_data);
self->data = self->entry (self->data);
return 0;
}
th->data = data;
th->entry = entry;
#if defined( UNDER_CE )
- hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
+ th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (th->cancel_event == NULL)
+ {
+ free(th);
+ return errno;
+ }
+ hThread = CreateThread (NULL, 128*1024, vlc_entry, th, CREATE_SUSPENDED, NULL);
#else
hThread = (HANDLE)(uintptr_t)
_beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
#if defined (LIBVLC_USE_PTHREAD_CANCEL)
pthread_cancel (thread_id);
#elif defined (UNDER_CE)
- /* HACK:There is no way to use something
- * like QueueUserAPC on Windows CE, so I rely
- * on some crappy arch specific code */
- CONTEXT context;
- context.ContextFlags = CONTEXT_CONTROL;
- GetThreadContext (thread_id->handle, &context);
- /* Setting the instruction pointer for the canceled thread */
-#if defined(_ARM_) || defined(ARM)
- context.Pc = (DWORD_PTR) vlc_cancel_self;
-#endif
- SetThreadContext (thread_id->handle, &context);
-
+ SetEvent (thread_id->cancel_event);
#elif defined (WIN32)
QueueUserAPC (vlc_cancel_self, thread_id->handle, 0);
#else
{
#if defined( LIBVLC_USE_PTHREAD )
int val = pthread_join (handle, result);
- if (val)
- vlc_thread_fatal ("joining thread", val, __func__, __FILE__, __LINE__);
+ VLC_THREAD_ASSERT ("joining thread");
#elif defined( UNDER_CE ) || defined( WIN32 )
do
CloseHandle (handle->handle);
if (result)
*result = handle->data;
+#if defined( UNDER_CE )
+ CloseHandle (handle->cancel_event);
+#endif
free (handle);
#endif
}
+/**
+ * Save the current cancellation state (enabled or disabled), then disable
+ * cancellation for the calling thread.
+ * This function must be called before entering a piece of code that is not
+ * cancellation-safe, unless it can be proven that the calling thread will not
+ * be cancelled.
+ * @return Previous cancellation state (opaque value for vlc_restorecancel()).
+ */
+int vlc_savecancel (void)
+{
+ int state;
+
+#if defined (LIBVLC_USE_PTHREAD_CANCEL)
+ int val = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
+ VLC_THREAD_ASSERT ("saving cancellation");
+
+#else
+ vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
+ if (nfo == NULL)
+ return false; /* Main thread - cannot be cancelled anyway */
+
+ state = nfo->killable;
+ nfo->killable = false;
+
+#endif
+ return state;
+}
+
+/**
+ * Restore the cancellation state for the calling thread.
+ * @param state previous state as returned by vlc_savecancel().
+ * @return Nothing, always succeeds.
+ */
+void vlc_restorecancel (int state)
+{
+#if defined (LIBVLC_USE_PTHREAD_CANCEL)
+# ifndef NDEBUG
+ int oldstate, val;
+
+ val = pthread_setcancelstate (state, &oldstate);
+ /* This should fail if an invalid value for given for state */
+ VLC_THREAD_ASSERT ("restoring cancellation");
+
+ if (oldstate != PTHREAD_CANCEL_DISABLE)
+ vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL,
+ __func__, __FILE__, __LINE__);
+# else
+ pthread_setcancelstate (state, NULL);
+# endif
+
+#else
+ vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
+ assert (state == false || state == true);
+
+ if (nfo == NULL)
+ return; /* Main thread - cannot be cancelled anyway */
+
+ assert (!nfo->killable);
+ nfo->killable = state != 0;
+
+#endif
+}
+
+/**
+ * Issues an explicit deferred cancellation point.
+ * This has no effect if thread cancellation is disabled.
+ * This can be called when there is a rather slow non-sleeping operation.
+ * This is also used to force a cancellation point in a function that would
+ * otherwise "not always" be a one (block_FifoGet() is an example).
+ */
+void vlc_testcancel (void)
+{
+#if defined (LIBVLC_USE_PTHREAD_CANCEL)
+ pthread_testcancel ();
+
+#else
+ vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
+ if (nfo == NULL)
+ return; /* Main thread - cannot be cancelled anyway */
+
+ if (nfo->killable && nfo->killed)
+ {
+ for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
+ p->proc (p->data);
+# if defined (LIBVLC_USE_PTHREAD)
+ pthread_exit (PTHREAD_CANCELLED);
+# elif defined (UNDER_CE)
+ ExitThread(0);
+# elif defined (WIN32)
+ _endthread ();
+# else
+# error Not implemented!
+# endif
+ }
+#endif
+}
+
struct vlc_thread_boot
{
return NULL;
}
+#undef vlc_thread_create
/*****************************************************************************
- * vlc_thread_create: create a thread, inner version
+ * vlc_thread_create: create a thread
*****************************************************************************
* 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, const char * psz_file, int i_line,
- const char *psz_name, void * ( *func ) ( vlc_object_t * ),
- int i_priority, bool b_wait )
+int vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
+ const char *psz_name, void *(*func) ( vlc_object_t * ),
+ int i_priority )
{
int i_ret;
vlc_object_internals_t *p_priv = vlc_internals( p_this );
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
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" );
-#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
- }
- }
+ msg_Dbg( p_this, "thread (%s) created at priority %d (%s:%d)",
+ psz_name, i_priority, psz_file, i_line );
else
{
p_priv->b_thread = false;
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)
#else
va_list ap;
- vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key);
+ vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
if (nfo == NULL)
{
#ifdef WIN32
if (nfo == NULL)
return; /* Uho! Expect problems! */
*nfo = VLC_CANCEL_INIT;
- vlc_threadvar_set (&cancel_key, nfo);
+ vlc_threadvar_set (cancel_key, nfo);
#endif
}
va_start (ap, cmd);
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);
-#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
-# error Not implemented!
-#endif
- }
- break;
-
case VLC_DO_CANCEL:
nfo->killed = true;
break;