[vlc-devel] [RFC] Thread cancellation on WinCE

Pierre Ynard linkfanel at yahoo.fr
Mon Nov 17 15:46:35 CET 2008


This patch adds support for thread cancellation on Windows CE, without
which stopping vlc quite doesn't work... Basically, WinCE lacks
functions such as WaitForSingleObjectEx, and the associated mechanism
to trigger the execution of a function from another thread. This patch
creates a cancellation event handle, and redefines WaitForSingleObjectEx
to wait on both the original handle and the cancellation event, which
can then be triggered from another thread.

This patch allows libvlc to shut down properly when nothing is playing.
However, there are still problems to stop input threads: condition
variable signaling between threads apparently causes problems, with or
without the present patch. I might not be able to debug this and make it
work myself.

There are also other functions like SleepEx in mtime.c and
WaitForMultipleObjectsEx in poll.c that ought to be handled. I am a
little unsure of the right way to organize the code, so comments more
than are welcome.


diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index 7b66e5b..0232dc7 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -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
diff --git a/src/misc/threads.c b/src/misc/threads.c
index 80872df..07bb426 100644
--- a/src/misc/threads.c
+++ b/src/misc/threads.c
@@ -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,62 @@ 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 (HANDLE handle, DWORD delay)
+{
+    vlc_cancel_t *nfo = vlc_threadvar_get (&cancel_key);
+    if (nfo == NULL)
+    {
+        /* Main thread - cannot be cancelled anyway */
+        return WaitForSingleObject (handle, delay);
+    }
+    const HANDLE handles[2] = { nfo->cancel_event, handle };
+    DWORD result = WaitForMultipleObjects (2, handles, FALSE, delay);
+    if (result == WAIT_OBJECT_0 + 1)
+    {
+        return WAIT_OBJECT_0;
+    }
+    else if (result == WAIT_ABANDONED_0 + 1)
+    {
+        return WAIT_ABANDONED;
+    }
+    else if (result == WAIT_TIMEOUT)
+    {
+        return WAIT_TIMEOUT;
+    }
+    else
+    {
+        vlc_cancel_self (NULL);
+        return WAIT_IO_COMPLETION;
+    }
+}
+
+DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds,
+                             BOOL bAlertable)
+{
+    if (bAlertable)
+    {
+        return vlc_cancelable_wait (hHandle, dwMilliseconds);
+    }
+    else
+    {
+        return WaitForSingleObject (hHandle, dwMilliseconds);
+    }
+}
 #endif
 
 #ifdef WIN32
@@ -391,10 +440,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 +581,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 +681,14 @@ 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;
+    }
+#endif
+#if defined( UNDER_CE )
     hThread = CreateThread (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
 #else
     hThread = (HANDLE)(uintptr_t)
@@ -694,18 +750,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


Regards,

-- 
Pierre Ynard
"Une âme dans un corps, c'est comme un dessin sur une feuille de papier."



More information about the vlc-devel mailing list