[vlc-devel] [RFCv2] Thread cancellation on WinCE
Pierre Ynard
linkfanel at yahoo.fr
Thu Nov 20 10:23:21 CET 2008
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.
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/mtime.c b/src/misc/mtime.c
index c5b1bc2..bc622c1 100644
--- a/src/misc/mtime.c
+++ b/src/misc/mtime.c
@@ -52,10 +52,6 @@
# 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
diff --git a/src/misc/threads.c b/src/misc/threads.c
index 80872df..048deff 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,97 @@ 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];
+ if (count > 0)
+ {
+ 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)
+{
+ /* We do not support the bWaitAll case */
+ if (bAlertable && ! bWaitAll)
+ {
+ return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds);
+ }
+ else
+ {
+ return WaitForMultipleObjects (nCount, lpHandles, bWaitAll,
+ dwMilliseconds);
+ }
+}
#endif
#ifdef WIN32
@@ -391,10 +475,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 +616,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 +716,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 +783,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 +816,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
--
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