[Android] [PATCH] WIP: emulate pthread cancellation without signals

Rafaël Carré funman at videolan.org
Sat Mar 10 15:13:03 CET 2012


---
Hi,

This seems to work fine although there are still a few items to do.

I would like to commit this here instead of the old buggy patch, I 
could get some useful feedback (i.e. your eventual crash reports), and
it might fix real bugs.


 ...-Android-add-compatibility-pthread_cancel.patch |  479 -------------------
 patches/0001-android-threads-support.patch         |  487 ++++++++++++++++++++
 2 files changed, 487 insertions(+), 479 deletions(-)
 delete mode 100644 patches/0001-Android-add-compatibility-pthread_cancel.patch
 create mode 100644 patches/0001-android-threads-support.patch

diff --git a/patches/0001-Android-add-compatibility-pthread_cancel.patch b/patches/0001-Android-add-compatibility-pthread_cancel.patch
deleted file mode 100644
index ea165f6..0000000
--- a/patches/0001-Android-add-compatibility-pthread_cancel.patch
+++ /dev/null
@@ -1,479 +0,0 @@
-From 1391ed6796be2ffe1625dfa05a6abbc8309726d4 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Jean-Philippe=20Andr=C3=A9?= <jpeg at videolan.org>
-Date: Thu, 2 Jun 2011 15:15:57 +0200
-Subject: [PATCH] Android: add compatibility pthread_cancel
-
-Original-code: c1ee19f63a6c6773400e98a9265b0a7e49fd528c
-               7884ed8d407647a1523afb8be6a2859477d54fd7
-               42336426297a2f0e38964f238208681a2b54d6d8
-               96cd0d6dbc510f656a9b962b2b17c68e7f19d02e
-               fd3a78c13899095f75021b1aeb38b68e6c1f0164
-               497c810426c2fc3d414ef8d98dd1ed321d86ece0
-Modified and ported-by: Jean-Baptiste Kempf <jb at videolan.org>
-Signed-off-by: Jean-Baptiste Kempf <jb at videolan.org>
----
- compat/pthread_cancel.c |  291 +++++++++++++++++++++++++++++++++++++++++++++++
- configure.ac            |    4 +
- include/vlc_fixups.h    |   25 ++++
- lib/error.c             |   16 +++
- src/posix/thread.c      |   25 ++++-
- 5 files changed, 360 insertions(+), 1 deletions(-)
- create mode 100644 compat/pthread_cancel.c
-
-diff --git a/compat/pthread_cancel.c b/compat/pthread_cancel.c
-new file mode 100644
-index 0000000..8a39f5d
---- /dev/null
-+++ b/compat/pthread_cancel.c
-@@ -0,0 +1,291 @@
-+/*****************************************************************************
-+ * pthread_cancel.c: pthread deferred cancellation replacement
-+ *****************************************************************************
-+ * Copyright © 2011 VideoLAN
-+ *
-+ * Author: Jean-Philippe André <jpeg # videolan.org>
-+ *
-+ * License: LGPLv2.1+
-+ *****************************************************************************/
-+
-+#ifdef HAVE_CONFIG_H
-+# include <config.h>
-+#endif
-+
-+#include <vlc_common.h>
-+#include <vlc_fixups.h>
-+#include <pthread.h>
-+#include <assert.h>
-+
-+/*
-+ * This file provide fixups for the following functions:
-+ * - pthread_cancel
-+ * - pthread_testcancel
-+ * - pthread_setcancelstate
-+ * As a result of the implementation design, we must also wrap
-+ * pthread_create into a cancellation-aware function
-+ */
-+
-+static void* thread_wrapper_routine (void *arg);
-+static void  thread_cancel_handler (int signal);
-+static void  thread_cancel_destructor (void *data);
-+
-+// Functions used by LibVLC
-+void pthread_cancel_initialize (void);
-+void pthread_cancel_deinitialize (void);
-+
-+/*
-+ * Some static variables used for initialization
-+ */
-+static pthread_key_t cancel_key = 0; /// Key for the thread-local variable
-+
-+/*
-+ * These objects define the thread-local variable tracking the thread's
-+ * cancellation status (cancellable, cancelled)
-+ */
-+typedef struct cancel_t cancel_t;
-+struct cancel_t
-+{
-+    int state;      /// PTHREAD_CANCEL_ENABLE (0) or PTHREAD_CANCEL_DISABLE (1)
-+    pthread_cond_t *cond; /// Non-null if thread waiting on cond
-+
-+    /* Booleans at the end for packing purposes */
-+    bool cancelled; /// Non-zero means this thread has been cancelled
-+};
-+
-+/*
-+ * This is the thread wrapper data
-+ */
-+typedef struct thread_wrapper_t thread_wrapper_t;
-+struct thread_wrapper_t
-+{
-+    void     *(*routine) (void*); /// Main thread routine to call
-+    void     *arg;                /// Argument to pass to the thread routine
-+    cancel_t *cancel; /// The cancel structure is allocated before the thread
-+};
-+
-+/**
-+ * Initialize pthread cancellation
-+ * Creates thread-local variable's key and catches SIGRTMIN in main thread
-+ **/
-+void pthread_cancel_initialize (void)
-+{
-+    // Set up signal handler
-+    struct sigaction act;
-+    memset (&act, 0, sizeof (act));
-+    sigemptyset (&act.sa_mask);
-+    act.sa_flags = 0;
-+    act.sa_handler = thread_cancel_handler;
-+    sigaction (SIGRTMIN, &act, NULL);
-+
-+    // Create thread-local variable key
-+    pthread_key_create (&cancel_key, thread_cancel_destructor);
-+}
-+
-+/**
-+ * Deinitialize pthread cancellation
-+ **/
-+void pthread_cancel_deinitialize (void)
-+{
-+    struct sigaction act;
-+    memset (&act, 0, sizeof (act));
-+    sigemptyset (&act.sa_mask);
-+    sigaction (SIGRTMIN, &act, NULL);
-+    pthread_key_delete (cancel_key);
-+    cancel_key = 0;
-+}
-+
-+/**
-+ * Replacement for pthread_cancel
-+ * Sends a SIGRTMIN signal to the thread
-+ **/
-+int pthread_cancel (pthread_t thread_id)
-+{
-+    return pthread_kill (thread_id, SIGRTMIN);
-+}
-+
-+/**
-+ * Replacement for pthread_create with support for cancellation
-+ **/
-+int pthread_create_cancel (pthread_t *thread_id,
-+                           const pthread_attr_t *attr,
-+                           void *(*routine) (void *),
-+                           void *arg)
-+{
-+    thread_wrapper_t *wrapper_data =
-+            (thread_wrapper_t*) calloc (1, sizeof (thread_wrapper_t));
-+    if (unlikely (!wrapper_data))
-+        return -1;
-+
-+    wrapper_data->routine = routine;
-+    wrapper_data->arg     = arg;
-+    wrapper_data->cancel  = (cancel_t*) calloc (1, sizeof (cancel_t));
-+
-+    if (unlikely (!wrapper_data->cancel))
-+    {
-+        free (wrapper_data);
-+        return -1;
-+    }
-+
-+    int ret = pthread_create (thread_id, attr, thread_wrapper_routine,
-+                              wrapper_data);
-+    if (unlikely (ret != 0))
-+    {
-+        // The thread wrapper should free these variables but it didn't start
-+        free (wrapper_data->cancel);
-+        free (wrapper_data);
-+    }
-+    return ret;
-+}
-+
-+/**
-+ * Thread wrapper
-+ * Sets up signal handlers and thread-local data before running the routine
-+ **/
-+static void* thread_wrapper_routine (void *arg)
-+{
-+    thread_wrapper_t *wrapper_data = (thread_wrapper_t*) arg;
-+
-+    // Set up signal handler
-+    struct sigaction act;
-+    memset (&act, 0, sizeof (act));
-+    sigemptyset (&act.sa_mask);
-+    act.sa_flags = 0;
-+    act.sa_handler = thread_cancel_handler;
-+    sigaction (SIGRTMIN, &act, NULL);
-+
-+    // Place specific data (cancel state stack)
-+    cancel_t *canc = wrapper_data->cancel;
-+    memset (canc, 0, sizeof (cancel_t));
-+    if (unlikely (pthread_setspecific (cancel_key, canc) != 0))
-+        return NULL;
-+
-+    void *ret = wrapper_data->routine (wrapper_data->arg);
-+    // Don't free wrapper_data->cancel. It will be destroyed automatically.
-+    free (wrapper_data);
-+    return ret;
-+}
-+
-+/**
-+ * Change thread's cancellation state (enable/disable)
-+ **/
-+int pthread_setcancelstate (int state, int *oldstate)
-+{
-+    cancel_t *canc = pthread_getspecific (cancel_key);
-+    if (unlikely (canc == NULL))
-+    {
-+        /// FIXME
-+        // Let's just add this missing variable since the main thread
-+        // uses these features but wasn't created by pthread_create_cancel
-+        canc = (cancel_t*) calloc (1, sizeof (cancel_t));
-+        pthread_setspecific (cancel_key, canc);
-+    }
-+
-+    if (oldstate)
-+        *oldstate = canc->state;
-+    canc->state = state;
-+
-+    if (state == PTHREAD_CANCEL_ENABLE)
-+        pthread_testcancel ();
-+
-+    return 0;
-+}
-+
-+/**
-+ * Create a cancellation point
-+ **/
-+void pthread_testcancel (void)
-+{
-+    cancel_t *canc = pthread_getspecific (cancel_key);
-+    if (unlikely (!canc))
-+        return; // Don't mess with the main thread
-+
-+    assert (canc->cond == NULL);
-+
-+    if (canc->cancelled) // Don't check PTHREAD_CANCEL_ENABLE
-+        pthread_exit (NULL);
-+}
-+
-+/**
-+ * Cancellation signal handler
-+ **/
-+static void thread_cancel_handler (int signal)
-+{
-+    assert (signal == SIGRTMIN);
-+
-+    cancel_t *canc = (cancel_t*) pthread_getspecific (cancel_key);
-+    if (unlikely (!canc))
-+        return; // Main thread, can't be cancelled
-+
-+    canc->cancelled = true;
-+    if (canc->cond)
-+    {
-+        /* Wakeup all threads waiting on cond. As we are supposed to expect
-+         * spurious wakeups, this should not be an issue
-+         */
-+        pthread_cond_t *cond = canc->cond;
-+        canc->cond = NULL;
-+        pthread_cond_broadcast (cond);
-+        /* FIXME: not calling pthread_exit (crashes in input thread). Why? */
-+        // pthread_exit (NULL);
-+        return;
-+    }
-+    if (canc->state == PTHREAD_CANCEL_ENABLE)
-+        pthread_exit (NULL);
-+}
-+
-+/**
-+ * Destroy a cancel_t variable. Nothing fancy.
-+ **/
-+static void thread_cancel_destructor (void *data)
-+{
-+    cancel_t *canc = (cancel_t*) data;
-+    free (canc);
-+}
-+
-+int pthread_cond_wait_cancel( pthread_cond_t *cond,
-+                                     pthread_mutex_t *mutex )
-+{
-+    int oldstate;
-+    pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
-+    cancel_t *canc = pthread_getspecific (cancel_key);
-+    if (canc)
-+    {
-+        assert (!canc->cond);
-+        canc->cond = cond;
-+    }
-+
-+    int ret = pthread_cond_wait (cond, mutex);
-+
-+    if (canc)
-+        canc->cond = NULL;
-+
-+    /// FIXME: We should call testcancel() here, but it leads to crashes.
-+    // pthread_testcancel ();
-+    pthread_setcancelstate (oldstate, NULL);
-+    return ret;
-+}
-+
-+int pthread_cond_timedwait_cancel( pthread_cond_t *cond,
-+                                   pthread_mutex_t *mutex,
-+                                   const struct timespec *abstime )
-+{
-+    int oldstate;
-+    pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
-+    cancel_t *canc = pthread_getspecific (cancel_key);
-+    if (canc)
-+    {
-+        assert (!canc->cond);
-+        canc->cond = cond;
-+    }
-+
-+    int ret = pthread_cond_timedwait (cond, mutex, abstime);
-+
-+    if (canc)
-+        canc->cond = NULL;
-+
-+    /// FIXME: We should call testcancel() here, but it leads to crashes.
-+    // pthread_testcancel ();
-+    pthread_setcancelstate (oldstate, NULL);
-+    return ret;
-+}
-diff --git a/configure.ac b/configure.ac
-index 5f99552..0f56da6 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -458,6 +458,10 @@ if test "${SYS}" = "mingw32" ; then
-     fi
- fi
- 
-+if test "${HAVE_ANDROID}" = "1"; then
-+    AC_REPLACE_FUNCS([pthread_cancel])
-+fi
-+
- dnl
- dnl Buggy glibc prevention. Purposedly not cached.
- dnl See sourceware.org bugs 5058 and 5443.
-diff --git a/include/vlc_fixups.h b/include/vlc_fixups.h
-index d91e155..a0eca1c 100644
---- a/include/vlc_fixups.h
-+++ b/include/vlc_fixups.h
-@@ -342,4 +342,29 @@ long nrand48 (unsigned short subi[3]);
- # undef HAVE_FORK   /* Implementation of fork() is imperfect on OS/2 */
- #endif
- 
-+#ifdef __ANDROID__
-+#ifndef HAVE_PTHREAD_CANCEL
-+#include <pthread.h>
-+enum
-+{
-+  PTHREAD_CANCEL_ENABLE,
-+  PTHREAD_CANCEL_DISABLE
-+};
-+extern void pthread_cancel_initialize (void);
-+extern void pthread_cancel_deinitialize (void);
-+extern int pthread_create_cancel( pthread_t *thread,
-+                           const pthread_attr_t *attr,
-+                           void *( *routine ) ( void* ),
-+                           void *arg );
-+extern int pthread_setcancelstate( int state, int *oldstate );
-+extern int pthread_cancel( pthread_t thread_id );
-+extern void pthread_testcancel( void );
-+extern int pthread_cond_wait_cancel( pthread_cond_t *cond,
-+                                     pthread_mutex_t *mutex );
-+extern int pthread_cond_timedwait_cancel( pthread_cond_t *cond,
-+                                          pthread_mutex_t *mutex,
-+                                          const struct timespec *abstime );
-+#endif // PTHREAD_CANCEL
-+#endif // __ANDROID__
-+
- #endif /* !LIBVLC_FIXUPS_H */
-diff --git a/lib/error.c b/lib/error.c
-index ef2ecdc..441953c 100644
---- a/lib/error.c
-+++ b/lib/error.c
-@@ -25,6 +25,11 @@
- #include <assert.h>
- #include <vlc/libvlc.h>
- 
-+#if !defined(HAVE_PTHREAD_CANCEL) && defined(__ANDROID__)
-+// Functions implemented in compat/pthread_cancel.c
-+extern void pthread_cancel_initialize( void );
-+extern void pthread_cancel_deinitialize( void );
-+#endif
- 
- static const char oom[] = "Out of memory";
- /* TODO: use only one thread-specific key for whole libvlc */
-@@ -39,13 +44,24 @@ static void libvlc_setup_threads (bool init)
-     if (init)
-     {
-         if (refs++ == 0)
-+        {
-             vlc_threadvar_create (&context, free);
-+
-+#if !defined(HAVE_PTHREAD_CANCEL) && defined(__ANDROID__)
-+            pthread_cancel_initialize ();
-+#endif
-+        }
-     }
-     else
-     {
-         assert (refs > 0);
-         if (--refs == 0)
-+        {
-             vlc_threadvar_delete (&context);
-+#if !defined(HAVE_PTHREAD_CANCEL) && defined(__ANDROID__)
-+            pthread_cancel_deinitialize ();
-+#endif
-+        }
-     }
-     vlc_mutex_unlock (&lock);
- }
-diff --git a/src/posix/thread.c b/src/posix/thread.c
-index a7a4873..1dd884e 100644
---- a/src/posix/thread.c
-+++ b/src/posix/thread.c
-@@ -146,7 +146,9 @@ void vlc_trace (const char *fn, const char *file, unsigned line)
- 
- static inline unsigned long vlc_threadid (void)
- {
--#if defined (__linux__)
-+#if defined (__ANDROID__)
-+     return syscall (__NR_gettid);
-+#elif defined (__linux__)
-      /* glibc does not provide a call for this */
-      return syscall (SYS_gettid);
- 
-@@ -428,7 +430,11 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
-  */
- void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
- {
-+#ifdef __ANDROID__
-+    int val = pthread_cond_wait_cancel( p_condvar, p_mutex );
-+#else
-     int val = pthread_cond_wait( p_condvar, p_mutex );
-+#endif
-     VLC_THREAD_ASSERT ("waiting on condition");
- }
- 
-@@ -451,7 +457,12 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
-                         mtime_t deadline)
- {
-     struct timespec ts = mtime_to_ts (deadline);
-+
-+#ifdef __ANDROID__
-+    int val = pthread_cond_timedwait_cancel (p_condvar, p_mutex, &ts);
-+#else
-     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
-+#endif
-     if (val != ETIMEDOUT)
-         VLC_THREAD_ASSERT ("timed-waiting on condition");
-     return val;
-@@ -490,6 +501,14 @@ void vlc_sem_destroy (vlc_sem_t *sem)
-     val = errno;
- #endif
- 
-+#ifdef __ANDROID__
-+    /* Bionic is so broken that it will return EBUSY on sem_destroy
-+     * if the semaphore has never been used...
-+     */
-+    if (likely(val == EBUSY))
-+        return; // It may be a real error, but there's no way to know
-+#endif
-+
-     VLC_THREAD_ASSERT ("destroying semaphore");
- }
- 
-@@ -720,7 +739,11 @@ static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
-     assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */
- #endif
- 
-+#ifndef __ANDROID__
-     ret = pthread_create (th, attr, entry, data);
-+#else
-+    ret = pthread_create_cancel (th, attr, entry, data);
-+#endif
-     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
-     pthread_attr_destroy (attr);
-     return ret;
--- 
-1.7.9.1
-
diff --git a/patches/0001-android-threads-support.patch b/patches/0001-android-threads-support.patch
new file mode 100644
index 0000000..d71d21a
--- /dev/null
+++ b/patches/0001-android-threads-support.patch
@@ -0,0 +1,487 @@
+From 1efcbc25a531c7166939fc7ddd6fc1e9575b1c2e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= <funman at videolan.org>
+Date: Sat, 10 Mar 2012 04:54:23 -0500
+Subject: [PATCH] android: threads support
+
+emulate pthread_cancel (based on win32 code)
+TODO:
+    - move initialization somewhere else
+
+    - use lock for cond/killed/killable?
+      -> ndk gcc doesn't support atomic ops nor __thread
+      -> read/writes should be atomic on arm (ldr/str)
+      -> cond atomic access should be guaranteed by using the cond's
+      mutex when signaling
+
+    - split thread.c to move specific code to own file:
+    affected functions are:
+     * vlc_cond_wait()
+     * vlc_cond_timedwait()
+     * vlc_clone_attr()
+     * vlc_join()
+     * vlc_cancel()
+     * vlc_savecancel()
+     * vlc_testcancel()
+     * vlc_restorecancel()
+     * vlc_control_cancel()
+
+timer, semaphores, rwlocks, mutexes, clock, mwait/msleep/mdate, threadvar
+are 100% shared with linux so it'd be useless to have 2 copies.
+---
+ include/vlc_threads.h |   13 +++
+ lib/error.c           |   16 ++++-
+ src/Makefile.am       |   17 ++++
+ src/posix/thread.c    |  205 +++++++++++++++++++++++++++++++++++++++++++------
+ 4 files changed, 226 insertions(+), 25 deletions(-)
+
+diff --git a/include/vlc_threads.h b/include/vlc_threads.h
+index ebf94e2..dad50b1 100644
+--- a/include/vlc_threads.h
++++ b/include/vlc_threads.h
+@@ -43,6 +43,15 @@
+ 
+ #   define pthread_sigmask  sigprocmask
+ 
++#elif defined( __ANDROID__ )      /* pthreads without pthread_cancel() */
++
++#   define LIBVLC_USE_PTHREAD 1
++
++#   include <unistd.h> /* _POSIX_SPIN_LOCKS */
++#   include <pthread.h>
++#   include <poll.h>
++#   include <semaphore.h>
++
+ #else                                         /* pthreads (like Linux & BSD) */
+ #   define LIBVLC_USE_PTHREAD 1
+ #   define LIBVLC_USE_PTHREAD_CANCEL 1
+@@ -118,7 +127,11 @@
+  *****************************************************************************/
+ 
+ #if defined (LIBVLC_USE_PTHREAD)
++# ifdef LIBVLC_USE_PTHREAD_CANCEL
+ typedef pthread_t       vlc_thread_t;
++# else
++typedef struct vlc_thread *vlc_thread_t;
++# endif
+ typedef pthread_mutex_t vlc_mutex_t;
+ #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
+ typedef pthread_cond_t  vlc_cond_t;
+diff --git a/lib/error.c b/lib/error.c
+index ef2ecdc..42f6b4e 100644
+--- a/lib/error.c
++++ b/lib/error.c
+@@ -35,17 +35,29 @@ static void libvlc_setup_threads (bool init)
+     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
+     static uintptr_t refs = 0;
+ 
++    void andro_init_threads(bool);
++
+     vlc_mutex_lock (&lock);
+     if (init)
+     {
+-        if (refs++ == 0)
++        if (refs++ == 0) {
++#ifdef __ANDROID__
++            /* XXX: move somewhere else? */
++            andro_init_threads(init);
++#endif
+             vlc_threadvar_create (&context, free);
++        }
+     }
+     else
+     {
+         assert (refs > 0);
+-        if (--refs == 0)
++        if (--refs == 0) {
+             vlc_threadvar_delete (&context);
++#ifdef __ANDROID__
++            /* XXX: move somewhere else? */
++            andro_init_threads(init);
++#endif
++        }
+     }
+     vlc_mutex_unlock (&lock);
+ }
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 81b01fb..122cdd5 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -196,6 +196,7 @@ libvlc_win32_rc.$(OBJEXT): libvlc_win32_rc.rc
+ EXTRA_libvlccore_la_SOURCES = \
+ 	$(SOURCES_libvlc_darwin) \
+ 	$(SOURCES_libvlc_linux) \
++	$(SOURCES_libvlc_android) \
+ 	$(SOURCES_libvlc_win32) \
+ 	$(SOURCES_libvlc_os2) \
+ 	$(SOURCES_libvlc_other) \
+@@ -206,6 +207,9 @@ EXTRA_libvlccore_la_SOURCES = \
+ if HAVE_DARWIN
+ libvlccore_la_SOURCES += $(SOURCES_libvlc_darwin)
+ else
++if HAVE_ANDROID
++libvlccore_la_SOURCES += $(SOURCES_libvlc_android)
++else
+ if HAVE_LINUX
+ libvlccore_la_SOURCES += $(SOURCES_libvlc_linux)
+ else
+@@ -228,6 +232,7 @@ endif
+ endif
+ endif
+ endif
++endif
+ if BUILD_HTTPD
+ libvlccore_la_SOURCES += $(SOURCES_libvlc_httpd)
+ endif
+@@ -248,6 +253,18 @@ SOURCES_libvlc_darwin = \
+ 	posix/rand.c \
+ 	$(NULL)
+ 
++SOURCES_libvlc_android = \
++	posix/dirs.c \
++	misc/atomic.c \
++	posix/filesystem.c \
++	posix/plugin.c \
++	posix/thread.c \
++	posix/linux_cpu.c \
++	posix/linux_specific.c \
++	posix/specific.c \
++	posix/rand.c \
++	$(NULL)
++
+ SOURCES_libvlc_linux = \
+ 	posix/dirs.c \
+ 	misc/atomic.c \
+diff --git a/src/posix/thread.c b/src/posix/thread.c
+index a7a4873..9fbd0d4 100644
+--- a/src/posix/thread.c
++++ b/src/posix/thread.c
+@@ -1,5 +1,5 @@
+ /*****************************************************************************
+- * thread.c : pthread back-end for LibVLC
++ * thread.c : android pthread back-end for LibVLC
+  *****************************************************************************
+  * Copyright (C) 1999-2009 VLC authors and VideoLAN
+  *
+@@ -43,6 +43,8 @@
+ #include <sched.h>
+ #include <sys/time.h> /* gettimeofday() */
+ 
++# include <android/log.h>
++
+ #ifdef __linux__
+ # include <sys/syscall.h> /* SYS_gettid */
+ #endif
+@@ -73,6 +75,17 @@
+ # define _POSIX_MONOTONIC_CLOCK (-1)
+ #endif
+ 
++
++#undef assert
++#define assert(x) do { \
++    if (!x) { \
++    __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d: %s", \
++        __FILE__, __LINE__, #x \
++        ); \
++        abort(); \
++    } \
++} while(0)
++
+ #if (_POSIX_TIMERS > 0)
+ static unsigned vlc_clock_prec;
+ 
+@@ -146,10 +159,11 @@ void vlc_trace (const char *fn, const char *file, unsigned line)
+ 
+ static inline unsigned long vlc_threadid (void)
+ {
+-#if defined (__linux__)
++#if defined (__ANDROID__)
++     return syscall (__NR_gettid);
++#elif defined (__linux__)
+      /* glibc does not provide a call for this */
+      return syscall (SYS_gettid);
+-
+ #else
+      union { pthread_t th; unsigned long int i; } v = { };
+      v.th = pthread_self ();
+@@ -169,7 +183,7 @@ vlc_thread_fatal (const char *action, int error,
+                   const char *function, const char *file, unsigned line)
+ {
+     int canc = vlc_savecancel ();
+-    fprintf (stderr, "LibVLC fatal error %s (%d) in thread %lu ",
++    __android_log_print(ANDROID_LOG_ERROR, "vlc", "LibVLC fatal error %s (%d) in thread %lu ",
+              action, error, vlc_threadid ());
+     vlc_trace (function, file, line);
+ 
+@@ -335,6 +349,53 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
+     VLC_THREAD_ASSERT ("unlocking mutex");
+ }
+ 
++struct vlc_thread
++{
++    pthread_t      thread;
++    pthread_cond_t *cond; /// Non-null if thread waiting on cond
++    pthread_mutex_t *lock ; /// Non-null if thread waiting on cond
++    vlc_cleanup_t *cleaners;
++
++    void *(*entry)(void*);
++    void *data;
++
++    bool killable;
++    bool killed;
++};
++
++static pthread_key_t thread_key = 0;
++
++/* XXX: move somewhere else? */
++void andro_init_threads(bool init)
++{
++    static struct vlc_thread main_thread = {
++        .cond     = NULL,
++        .lock     = NULL,
++        .cleaners = NULL,
++        .killable = false,
++        .killed   = false,
++        .entry    = NULL,
++        .data     = NULL,
++    };
++
++    if (init) {
++        main_thread.thread = pthread_self();
++        pthread_key_create(&thread_key, NULL);
++        if (pthread_setspecific(thread_key, &main_thread))
++            abort();
++    } else {
++        pthread_key_delete(thread_key);
++    }
++}
++
++static void *andro_Thread(void *data)
++{
++    vlc_thread_t thread = data;
++    if (pthread_setspecific(thread_key, thread))
++        abort();
++    return thread->entry(thread->data);
++}
++
+ /**
+  * Initializes a condition variable.
+  */
+@@ -428,7 +489,22 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
+  */
+ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
+ {
++    vlc_thread_t thread = pthread_getspecific(thread_key);
++
++    if (thread) {
++        vlc_testcancel();
++        thread->cond = p_condvar;
++        thread->lock = p_mutex;
++    }
++
+     int val = pthread_cond_wait( p_condvar, p_mutex );
++
++    if (thread) {
++        thread->cond = NULL;
++        thread->lock = NULL;
++        vlc_testcancel();
++    }
++
+     VLC_THREAD_ASSERT ("waiting on condition");
+ }
+ 
+@@ -450,10 +526,25 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
+ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
+                         mtime_t deadline)
+ {
++    vlc_thread_t thread = pthread_getspecific(thread_key);
+     struct timespec ts = mtime_to_ts (deadline);
++
++    if (thread) {
++        vlc_testcancel();
++        thread->cond = p_condvar;
++        thread->lock = p_mutex;
++    }
++
+     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
+     if (val != ETIMEDOUT)
+         VLC_THREAD_ASSERT ("timed-waiting on condition");
++
++    if (thread) {
++        thread->cond = NULL;
++        thread->lock = NULL;
++        vlc_testcancel();
++    }
++
+     return val;
+ }
+ 
+@@ -720,7 +811,21 @@ static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
+     assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */
+ #endif
+ 
+-    ret = pthread_create (th, attr, entry, data);
++    vlc_thread_t thread = malloc (sizeof (*thread));
++    if (unlikely(thread == NULL))
++        return ENOMEM;
++
++    thread->killable = true;
++    thread->killed = false;
++    thread->cond = NULL;
++    thread->lock = NULL;
++    thread->cleaners = NULL;
++    thread->entry = entry;
++    thread->data = data;
++
++    *th = thread;
++    ret = pthread_create (&thread->thread, attr, andro_Thread, thread);
++
+     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
+     pthread_attr_destroy (attr);
+     return ret;
+@@ -761,8 +866,9 @@ int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
+  */
+ void vlc_join (vlc_thread_t handle, void **result)
+ {
+-    int val = pthread_join (handle, result);
++    int val = pthread_join (handle->thread, result);
+     VLC_THREAD_ASSERT ("joining thread");
++    free(handle);
+ }
+ 
+ /**
+@@ -828,6 +934,7 @@ int vlc_set_priority (vlc_thread_t th, int priority)
+             return VLC_EGENERIC;
+     }
+ #else
++    (void) th;
+     (void) priority;
+ #endif
+     return VLC_SUCCESS;
+@@ -842,10 +949,25 @@ int vlc_set_priority (vlc_thread_t th, int priority)
+  */
+ void vlc_cancel (vlc_thread_t thread_id)
+ {
+-    pthread_cancel (thread_id);
+-#ifdef HAVE_MAEMO
+-    pthread_kill (thread_id, SIGRTMIN);
+-#endif
++    bool self = thread_id == pthread_getspecific(thread_key);
++
++    thread_id->killed = true;
++    if (!thread_id->killable)
++        return;
++
++    vlc_mutex_t *lock = thread_id->lock;
++
++    if (lock) {
++        if (!self)
++            vlc_mutex_lock(lock);
++        if (thread_id->cond)
++            pthread_cond_broadcast(thread_id->cond);
++        if (!self)
++            vlc_mutex_unlock(lock);
++    }
++
++    if (self)
++        vlc_testcancel();
+ }
+ 
+ /**
+@@ -858,11 +980,13 @@ void vlc_cancel (vlc_thread_t thread_id)
+  */
+ int vlc_savecancel (void)
+ {
+-    int state;
+-    int val = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
++    vlc_thread_t thread = pthread_getspecific(thread_key);
++    if (!thread) /* not created by VLC, can't be cancelled */
++        return true;
+ 
+-    VLC_THREAD_ASSERT ("saving cancellation");
+-    return state;
++    int oldstate = thread->killable;
++    thread->killable = false;
++    return oldstate;
+ }
+ 
+ /**
+@@ -872,18 +996,19 @@ int vlc_savecancel (void)
+  */
+ void vlc_restorecancel (int state)
+ {
++    vlc_thread_t thread = pthread_getspecific(thread_key);
++    if (!thread) /* not created by VLC, can't be cancelled */
++        return;
+ #ifndef NDEBUG
+-    int oldstate, val;
++    int oldstate = thread->killable;
+ 
+-    val = pthread_setcancelstate (state, &oldstate);
+-    /* This should fail if an invalid value for given for state */
+-    VLC_THREAD_ASSERT ("restoring cancellation");
++    thread->killable = state;
+ 
+-    if (unlikely(oldstate != PTHREAD_CANCEL_DISABLE))
++    if (unlikely(oldstate != false))
+          vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL,
+                            __func__, __FILE__, __LINE__);
+ #else
+-    pthread_setcancelstate (state, NULL);
++    thread->killable = state;
+ #endif
+ }
+ 
+@@ -896,13 +1021,47 @@ void vlc_restorecancel (int state)
+  */
+ void vlc_testcancel (void)
+ {
+-    pthread_testcancel ();
++    vlc_thread_t thread = pthread_getspecific(thread_key);
++    if (!thread) /* not created by VLC, can't be cancelled */
++        return;
++    if (!thread->killable || !thread->killed)
++        return;
++
++    for (vlc_cleanup_t *p = thread->cleaners; p != NULL; p = p->next)
++        p->proc (p->data);
++
++    pthread_exit(NULL);
+ }
+ 
+ void vlc_control_cancel (int cmd, ...)
+ {
+-    (void) cmd;
+-    assert (0);
++    vlc_thread_t thread = pthread_getspecific(thread_key);
++    if (!thread) /* not created by VLC, can't be cancelled */
++        return;
++    /* NOTE: This function only modifies thread-specific data, so there is no
++     * need to lock anything. */
++    va_list ap;
++
++    va_start (ap, cmd);
++    switch (cmd)
++    {
++        case VLC_CLEANUP_PUSH:
++        {
++            /* cleaner is a pointer to the caller stack, no need to allocate
++             * and copy anything. As a nice side effect, this cannot fail. */
++            vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
++            cleaner->next = thread->cleaners;
++            thread->cleaners = cleaner;
++            break;
++        }
++
++        case VLC_CLEANUP_POP:
++        {
++            thread->cleaners = thread->cleaners->next;
++            break;
++        }
++    }
++    va_end (ap);
+ }
+ 
+ /**
+-- 
+1.7.9.1
+
-- 
1.7.9.1


More information about the Android mailing list