[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