Thread cancellation on WinCE
authorPierre Ynard <linkfanel@yahoo.fr>
Tue, 25 Nov 2008 13:22:24 +0000 (14:22 +0100)
committerRémi Denis-Courmont <rdenis@simphalempin.com>
Tue, 25 Nov 2008 16:58:32 +0000 (18:58 +0200)
This patch adds support for thread cancellation on Windows CE. It
emulates missing functions such as WaitForSingleObjectEx, by creating
a cancellation event handle, and forwarding the wait functions to wait
on both the original handles and the cancellation event. The thread can
then be cancelled by triggering the event from another thread.

Signed-off-by: Rémi Denis-Courmont <rdenis@simphalempin.com>
include/vlc_threads.h
src/misc/mtime.c
src/misc/threads.c

index 7b66e5b..0232dc7 100644 (file)
@@ -115,6 +115,9 @@ typedef struct
     HANDLE handle;
     void  *(*entry) (void *);
     void  *data;
+#if defined( UNDER_CE )
+    HANDLE cancel_event;
+#endif
 } *vlc_thread_t;
 
 typedef struct
index c5b1bc2..bc622c1 100644 (file)
 #   include <mmsystem.h>
 #endif
 
-#if defined( UNDER_CE )
-#   define SleepEx(a,b)  Sleep(a)
-#endif
-
 #if defined(HAVE_SYS_TIME_H)
 #   include <sys/time.h>
 #endif
index 80872df..c7d7957 100644 (file)
@@ -49,10 +49,6 @@ static vlc_threadvar_t cancel_key;
 # include <execinfo.h>
 #endif
 
-#ifdef UNDER_CE
-# define WaitForSingleObjectEx(a,b,c) WaitForSingleObject(a,b)
-#endif
-
 /**
  * Print a backtrace to the standard error for debugging purpose.
  */
@@ -144,9 +140,95 @@ typedef struct vlc_cancel_t
     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
@@ -391,10 +473,6 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
 #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
@@ -536,6 +614,9 @@ 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);
     self->data = self->entry (self->data);
@@ -633,6 +714,12 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
     th->data = data;
     th->entry = entry;
 #if defined( UNDER_CE )
+    th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+    if (th->cancel_event == NULL)
+    {
+        free(th);
+        return errno;
+    }
     hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
 #else
     hThread = (HANDLE)(uintptr_t)
@@ -694,18 +781,7 @@ void vlc_cancel (vlc_thread_t thread_id)
 #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
@@ -738,6 +814,9 @@ void vlc_join (vlc_thread_t handle, void **result)
     CloseHandle (handle->handle);
     if (result)
         *result = handle->data;
+#if defined( UNDER_CE )
+    CloseHandle (handle->cancel_event);
+#endif
     free (handle);
 
 #endif