]> git.sesse.net Git - vlc/blobdiff - src/android/thread.c
vout: remove unncessary lock in vout_HoldPicture()
[vlc] / src / android / thread.c
index e8b361c07973f04de49310250b2c083afa9da842..dbf2ece9cb43be229949a8471cb4f234b8d7edce 100644 (file)
@@ -35,6 +35,7 @@
 #include <signal.h>
 #include <errno.h>
 #include <time.h>
+#include <assert.h>
 
 #include <sys/types.h>
 #include <unistd.h> /* fsync() */
@@ -44,9 +45,9 @@
 #include <android/log.h>
 #include <sys/syscall.h> /* __NR_gettid */
 
-/* FIXME: Android has a monotonic clock
- * XXX : how to use it with pthread_cond_wait() ? */
-# warning Monotonic clock not available. Expect timing issues.
+#if !defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && !defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP)
+#error no pthread monotonic clock support
+#endif
 
 /* helper */
 static struct timespec mtime_to_ts (mtime_t date)
@@ -168,14 +169,13 @@ struct vlc_thread
     pthread_t      thread;
     pthread_cond_t *cond; /// Non-null if thread waiting on cond
     vlc_mutex_t    lock ; /// Protects cond
+    vlc_sem_t      finished;
 
     void *(*entry)(void*);
     void *data;
 
-    vlc_atomic_t killed;
-    vlc_atomic_t finished;
+    atomic_bool killed;
     bool killable;
-    bool detached;
 };
 
 static __thread struct vlc_thread *thread = NULL;
@@ -185,103 +185,199 @@ void vlc_threads_setup (libvlc_int_t *p_libvlc)
     (void)p_libvlc;
 }
 
-static void *andro_Thread(void *data)
-{
-    thread = data;
-    void *ret = thread->entry(thread->data);
-    if (thread->detached) {
-        /* release thread handle */
-        vlc_mutex_destroy(&thread->lock);
-        free(thread);
-    } else {
-        vlc_atomic_set(&thread->finished, true);
-        /* thread handle will be freed when vlc_join() is called */
-    }
-    return ret;
-}
-
 /* cond */
 
-void vlc_cond_init (vlc_cond_t *p_condvar)
+void vlc_cond_init (vlc_cond_t *condvar)
 {
-    if (unlikely(pthread_cond_init (p_condvar, NULL)))
+#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
+    if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
         abort ();
+#else
+    pthread_condattr_t attr;
+
+    pthread_condattr_init (&attr);
+    pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
+
+    if (unlikely(pthread_cond_init (&condvar->cond, &attr)))
+        abort ();
+#endif
+    condvar->clock = CLOCK_MONOTONIC;
 }
 
-void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
+void vlc_cond_init_daytime (vlc_cond_t *condvar)
 {
-    vlc_cond_init(p_condvar);
+    if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
+        abort ();
+    condvar->clock = CLOCK_REALTIME;
 }
 
-void vlc_cond_destroy (vlc_cond_t *p_condvar)
+void vlc_cond_destroy (vlc_cond_t *condvar)
 {
-    int val = pthread_cond_destroy( p_condvar );
+    int val = pthread_cond_destroy (&condvar->cond);
     VLC_THREAD_ASSERT ("destroying condition");
 }
 
-void vlc_cond_signal (vlc_cond_t *p_condvar)
+void vlc_cond_signal (vlc_cond_t *condvar)
 {
-    int val = pthread_cond_signal( p_condvar );
+    int val = pthread_cond_signal (&condvar->cond);
     VLC_THREAD_ASSERT ("signaling condition variable");
 }
 
-void vlc_cond_broadcast (vlc_cond_t *p_condvar)
+void vlc_cond_broadcast (vlc_cond_t *condvar)
 {
-    pthread_cond_broadcast (p_condvar);
+    pthread_cond_broadcast (&condvar->cond);
 }
 
-void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
+void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
 {
-    if (thread) {
-        vlc_testcancel();
-        vlc_mutex_lock(&thread->lock);
-        thread->cond = p_condvar;
-        vlc_mutex_unlock(&thread->lock);
+    vlc_thread_t th = thread;
+
+    if (th != NULL)
+    {
+        vlc_testcancel ();
+        if (vlc_mutex_trylock (&th->lock) == 0)
+        {
+            th->cond = &condvar->cond;
+            vlc_mutex_unlock (&th->lock);
+        }
+        else
+        {   /* The lock is already held by another thread.
+             * => That other thread has just cancelled this one. */
+            vlc_testcancel ();
+            /* Cancellation did not occur even though this thread is cancelled.
+             * => Cancellation is disabled. */
+            th = NULL;
+        }
     }
 
-    int val = pthread_cond_wait( p_condvar, p_mutex );
+    int val = pthread_cond_wait (&condvar->cond, p_mutex);
+    VLC_THREAD_ASSERT ("waiting on condition");
 
-    if (thread) {
-        vlc_mutex_lock(&thread->lock);
-        thread->cond = NULL;
-        vlc_mutex_unlock(&thread->lock);
+    if (th != NULL)
+    {
+        if (vlc_mutex_trylock (&th->lock) == 0)
+        {
+            thread->cond = NULL;
+            vlc_mutex_unlock (&th->lock);
+        }
+        /* Else: This thread was cancelled and is cancellable.
+                 vlc_testcancel() will take of it right there: */
         vlc_testcancel();
     }
-
-    VLC_THREAD_ASSERT ("waiting on condition");
 }
 
-int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
+int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
                         mtime_t deadline)
 {
     struct timespec ts = mtime_to_ts (deadline);
+    vlc_thread_t th = thread;
+#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
+    int (*cb)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
+#endif
 
-    if (thread) {
-        vlc_testcancel();
-        vlc_mutex_lock(&thread->lock);
-        thread->cond = p_condvar;
-        vlc_mutex_unlock(&thread->lock);
+    if (th != NULL)
+    {
+        vlc_testcancel ();
+        if (vlc_mutex_trylock (&th->lock) == 0)
+        {
+            th->cond = &condvar->cond;
+            vlc_mutex_unlock (&th->lock);
+        }
+        else
+        {   /* The lock is already held by another thread.
+             * => That other thread has just cancelled this one. */
+            vlc_testcancel ();
+            /* Cancellation did not occur even though this thread is cancelled.
+             * => Cancellation is disabled. */
+            th = NULL;
+        }
     }
 
-    int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
+#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
+    switch (condvar->clock)
+    {
+         case CLOCK_REALTIME:
+             cb = pthread_cond_timedwait;
+             break;
+         case CLOCK_MONOTONIC:
+             cb = pthread_cond_timedwait_monotonic_np;
+             break;
+         default:
+             assert (0);
+    }
+
+    int val = cb (&condvar->cond, p_mutex, &ts);
+#else
+    int val = pthread_cond_timedwait(&condvar->cond, p_mutex, &ts);
+#endif
+
     if (val != ETIMEDOUT)
         VLC_THREAD_ASSERT ("timed-waiting on condition");
 
-    if (thread) {
-        vlc_mutex_lock(&thread->lock);
-        thread->cond = NULL;
-        vlc_mutex_unlock(&thread->lock);
+    if (th != NULL)
+    {
+        if (vlc_mutex_trylock (&th->lock) == 0)
+        {
+            thread->cond = NULL;
+            vlc_mutex_unlock (&th->lock);
+        }
+        /* Else: This thread was cancelled and is cancellable.
+                 vlc_testcancel() will take of it right there: */
         vlc_testcancel();
     }
-
     return val;
 }
 
 /* pthread */
+static void clean_detached_thread(void *data)
+{
+    struct vlc_thread *thread = data;
+
+    /* release thread handle */
+    vlc_mutex_destroy(&thread->lock);
+    free(thread);
+}
+
+static void *detached_thread(void *data)
+{
+    vlc_thread_t th = data;
+
+    thread = th;
+
+    vlc_cleanup_push(clean_detached_thread, data);
+    th->entry(th->data);
+    vlc_cleanup_run();
 
-static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
-                           void *(*entry) (void *), void *data, int priority)
+    return NULL;
+}
+
+static void finish_joinable_thread(void *data)
+{
+    vlc_thread_t th = data;
+
+    vlc_sem_post(&th->finished);
+}
+
+static void *joinable_thread(void *data)
+{
+    vlc_thread_t th = data;
+    void *ret;
+
+    vlc_cleanup_push(finish_joinable_thread, th);
+    thread = th;
+    ret = th->entry(th->data);
+    vlc_cleanup_run();
+
+    return ret;
+}
+
+static int vlc_clone_attr (vlc_thread_t *th, void *(*entry) (void *),
+                           void *data, bool detach)
 {
+    vlc_thread_t thread = malloc (sizeof (*thread));
+    if (unlikely(thread == NULL))
+        return ENOMEM;
+
     int ret;
 
     sigset_t oldset;
@@ -297,50 +393,40 @@ static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
         pthread_sigmask (SIG_BLOCK, &set, &oldset);
     }
 
-    (void) priority;
-
-    vlc_thread_t thread = malloc (sizeof (*thread));
-    if (unlikely(thread == NULL)) {
-        if (attr)
-            pthread_attr_destroy(attr);
-        return ENOMEM;
-    }
-
-    vlc_atomic_set(&thread->killed, false);
-    vlc_atomic_set(&thread->finished, false);
+    if (!detach)
+        vlc_sem_init(&thread->finished, 0);
+    atomic_store(&thread->killed, false);
     thread->killable = true;
-    int state = PTHREAD_CREATE_JOINABLE;
-    if (attr)
-        pthread_attr_getdetachstate(attr, &state);
-    thread->detached = state == PTHREAD_CREATE_DETACHED;
     thread->cond = NULL;
     thread->entry = entry;
     thread->data = data;
     vlc_mutex_init(&thread->lock);
 
-    *th = thread;
-    ret = pthread_create (&thread->thread, attr, andro_Thread, thread);
+    pthread_attr_t attr;
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, detach ? PTHREAD_CREATE_DETACHED
+                                               : PTHREAD_CREATE_JOINABLE);
+
+    ret = pthread_create (&thread->thread, &attr,
+                          detach ? detached_thread : joinable_thread, thread);
+    pthread_attr_destroy (&attr);
 
     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
-    if (attr)
-        pthread_attr_destroy (attr);
+    *th = thread;
     return ret;
 }
 
 int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
                int priority)
 {
-    pthread_attr_t attr;
-
-    pthread_attr_init (&attr);
-    return vlc_clone_attr (th, &attr, entry, data, priority);
+    (void) priority;
+    return vlc_clone_attr (th, entry, data, false);
 }
 
 void vlc_join (vlc_thread_t handle, void **result)
 {
-    vlc_testcancel();
-    while (!vlc_atomic_get(&handle->finished))
-        msleep(CLOCK_FREQ / 100);
+    vlc_sem_wait (&handle->finished);
+    vlc_sem_destroy (&handle->finished);
 
     int val = pthread_join (handle->thread, result);
     VLC_THREAD_ASSERT ("joining thread");
@@ -352,14 +438,11 @@ int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void *data,
                       int priority)
 {
     vlc_thread_t dummy;
-    pthread_attr_t attr;
-
     if (th == NULL)
         th = &dummy;
 
-    pthread_attr_init (&attr);
-    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-    return vlc_clone_attr (th, &attr, entry, data, priority);
+    (void) priority;
+    return vlc_clone_attr (th, entry, data, true);
 }
 
 int vlc_set_priority (vlc_thread_t th, int priority)
@@ -370,11 +453,12 @@ int vlc_set_priority (vlc_thread_t th, int priority)
 
 void vlc_cancel (vlc_thread_t thread_id)
 {
-    vlc_atomic_set(&thread_id->killed, true);
+    pthread_cond_t *cond;
 
-    vlc_mutex_lock(&thread_id->lock);
-    vlc_cond_t *cond = thread_id->cond;
+    atomic_store(&thread_id->killed, true);
 
+    vlc_mutex_lock(&thread_id->lock);
+    cond = thread_id->cond;
     if (cond)
         pthread_cond_broadcast(cond);
     vlc_mutex_unlock(&thread_id->lock);
@@ -385,7 +469,7 @@ int vlc_savecancel (void)
     if (!thread) /* not created by VLC, can't be cancelled */
         return true;
 
-    int oldstate = vlc_atomic_get(&thread->killable);
+    int oldstate = thread->killable;
     thread->killable = false;
     return oldstate;
 }
@@ -404,10 +488,9 @@ void vlc_testcancel (void)
         return;
     if (!thread->killable)
         return;
-    if (!vlc_atomic_get(&thread->killed))
+    if (!atomic_load(&thread->killed))
         return;
 
-    vlc_atomic_set(&thread->finished, true);
     pthread_exit(NULL);
 }
 
@@ -438,7 +521,7 @@ mtime_t mdate (void)
 {
     struct timespec ts;
 
-    if (unlikely(clock_gettime (CLOCK_REALTIME, &ts) != 0))
+    if (unlikely(clock_gettime (CLOCK_MONOTONIC, &ts) != 0))
         abort ();
 
     return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
@@ -447,34 +530,25 @@ mtime_t mdate (void)
 #undef mwait
 void mwait (mtime_t deadline)
 {
-    deadline -= mdate ();
-    if (deadline > 0)
-        msleep (deadline);
+    vlc_mutex_t lock;
+    vlc_cond_t wait;
+
+    vlc_mutex_init (&lock);
+    vlc_cond_init (&wait);
+
+    vlc_mutex_lock (&lock);
+    mutex_cleanup_push (&lock);
+    while (!vlc_cond_timedwait (&wait, &lock, deadline));
+    vlc_cleanup_run ();
+
+    vlc_cond_destroy (&wait);
+    vlc_mutex_destroy (&lock);
 }
 
 #undef msleep
 void msleep (mtime_t delay)
 {
-    struct timespec ts = mtime_to_ts (delay);
-
-    vlc_testcancel();
-    for (;;) {
-        /* FIXME: drift */
-        struct timespec t = { 0, 10 * 1000 * 1000 };
-        if (ts.tv_sec <= 0 && t.tv_nsec > ts.tv_nsec)
-            t.tv_nsec = ts.tv_nsec;
-        while (nanosleep (&t, &t) == -1) {
-            vlc_testcancel();
-            vlc_assert (errno == EINTR);
-        }
-
-        ts.tv_nsec -= 10 * 1000 * 1000;
-        if (ts.tv_nsec < 0) {
-            if (--ts.tv_sec < 0)
-                return;
-            ts.tv_nsec += 1000 * 1000 * 1000;
-        }
-    }
+    mwait (mdate () + delay);
 }
 
 /* cpu */