#include <signal.h>
#include <errno.h>
#include <time.h>
+#include <assert.h>
#include <sys/types.h>
#include <unistd.h> /* fsync() */
#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)
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;
(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;
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");
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)
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);
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;
}
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);
}
{
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);
#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 */