1 /*****************************************************************************
2 * thread.c : android pthread back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2012 VLC authors and VideoLAN
6 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
7 * Samuel Hocevar <sam@zoy.org>
8 * Gildas Bazin <gbazin@netcourrier.com>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_atomic.h>
40 #include <sys/types.h>
41 #include <unistd.h> /* fsync() */
45 #include <android/log.h>
46 #include <sys/syscall.h> /* __NR_gettid */
49 static struct timespec mtime_to_ts (mtime_t date)
51 lldiv_t d = lldiv (date, CLOCK_FREQ);
52 struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
58 #define vlc_assert(x) do { \
60 __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d: %s", \
61 __FILE__, __LINE__, #x \
69 vlc_thread_fatal (const char *action, int error,
70 const char *function, const char *file, unsigned line)
75 switch (strerror_r (error, buf, sizeof (buf)))
80 case ERANGE: /* should never happen */
81 msg = "unknown (too big to display)";
84 msg = "unknown (invalid error number)";
88 __android_log_print(ANDROID_LOG_ERROR, "vlc",
89 "LibVLC fatal error %s (%d) in thread %d "
90 "at %s:%u in %s\n Error message: %s\n",
91 action, error, syscall (__NR_gettid), file, line, function, msg);
96 # define VLC_THREAD_ASSERT( action ) \
98 vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
100 # define VLC_THREAD_ASSERT( action ) ((void)val)
104 void vlc_mutex_init( vlc_mutex_t *p_mutex )
106 pthread_mutexattr_t attr;
108 pthread_mutexattr_init (&attr);
110 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_DEFAULT);
112 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
114 pthread_mutex_init (p_mutex, &attr);
115 pthread_mutexattr_destroy( &attr );
118 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
120 pthread_mutexattr_t attr;
122 pthread_mutexattr_init (&attr);
123 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
124 pthread_mutex_init (p_mutex, &attr);
125 pthread_mutexattr_destroy( &attr );
129 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
131 int val = pthread_mutex_destroy( p_mutex );
132 VLC_THREAD_ASSERT ("destroying mutex");
136 void vlc_assert_locked (vlc_mutex_t *p_mutex)
138 vlc_assert (pthread_mutex_lock (p_mutex) == EDEADLK);
142 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
144 int val = pthread_mutex_lock( p_mutex );
145 VLC_THREAD_ASSERT ("locking mutex");
148 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
150 int val = pthread_mutex_trylock( p_mutex );
153 VLC_THREAD_ASSERT ("locking mutex");
157 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
159 int val = pthread_mutex_unlock( p_mutex );
160 VLC_THREAD_ASSERT ("unlocking mutex");
166 pthread_cond_t *cond; /// Non-null if thread waiting on cond
167 vlc_mutex_t lock ; /// Protects cond
170 void *(*entry)(void*);
177 static __thread struct vlc_thread *thread = NULL;
179 void vlc_threads_setup (libvlc_int_t *p_libvlc)
186 void vlc_cond_init (vlc_cond_t *condvar)
188 if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
190 condvar->clock = CLOCK_MONOTONIC;
193 void vlc_cond_init_daytime (vlc_cond_t *condvar)
195 if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
197 condvar->clock = CLOCK_REALTIME;
200 void vlc_cond_destroy (vlc_cond_t *condvar)
202 int val = pthread_cond_destroy (&condvar->cond);
203 VLC_THREAD_ASSERT ("destroying condition");
206 void vlc_cond_signal (vlc_cond_t *condvar)
208 int val = pthread_cond_signal (&condvar->cond);
209 VLC_THREAD_ASSERT ("signaling condition variable");
212 void vlc_cond_broadcast (vlc_cond_t *condvar)
214 pthread_cond_broadcast (&condvar->cond);
217 void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
219 vlc_thread_t th = thread;
224 if (vlc_mutex_trylock (&th->lock) == 0)
226 th->cond = &condvar->cond;
227 vlc_mutex_unlock (&th->lock);
230 { /* The lock is already held by another thread.
231 * => That other thread has just cancelled this one. */
233 /* Cancellation did not occur even though this thread is cancelled.
234 * => Cancellation is disabled. */
239 int val = pthread_cond_wait (&condvar->cond, p_mutex);
240 VLC_THREAD_ASSERT ("waiting on condition");
244 if (vlc_mutex_trylock (&th->lock) == 0)
247 vlc_mutex_unlock (&th->lock);
249 /* Else: This thread was cancelled and is cancellable.
250 vlc_testcancel() will take of it right there: */
255 int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
258 struct timespec ts = mtime_to_ts (deadline);
259 vlc_thread_t th = thread;
260 int (*cb)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
265 if (vlc_mutex_trylock (&th->lock) == 0)
267 th->cond = &condvar->cond;
268 vlc_mutex_unlock (&th->lock);
271 { /* The lock is already held by another thread.
272 * => That other thread has just cancelled this one. */
274 /* Cancellation did not occur even though this thread is cancelled.
275 * => Cancellation is disabled. */
280 switch (condvar->clock)
283 cb = pthread_cond_timedwait;
285 case CLOCK_MONOTONIC:
286 cb = pthread_cond_timedwait_monotonic_np;
292 int val = cb (&condvar->cond, p_mutex, &ts);
293 if (val != ETIMEDOUT)
294 VLC_THREAD_ASSERT ("timed-waiting on condition");
298 if (vlc_mutex_trylock (&th->lock) == 0)
301 vlc_mutex_unlock (&th->lock);
303 /* Else: This thread was cancelled and is cancellable.
304 vlc_testcancel() will take of it right there: */
311 static void clean_detached_thread(void *data)
313 struct vlc_thread *thread = data;
315 /* release thread handle */
316 vlc_mutex_destroy(&thread->lock);
320 static void *detached_thread(void *data)
322 vlc_thread_t th = data;
326 vlc_cleanup_push(clean_detached_thread, data);
333 static void finish_joinable_thread(void *data)
335 vlc_thread_t th = data;
337 vlc_sem_post(&th->finished);
340 static void *joinable_thread(void *data)
342 vlc_thread_t th = data;
345 vlc_cleanup_push(finish_joinable_thread, th);
347 ret = th->entry(th->data);
353 static int vlc_clone_attr (vlc_thread_t *th, void *(*entry) (void *),
354 void *data, bool detach)
356 vlc_thread_t thread = malloc (sizeof (*thread));
357 if (unlikely(thread == NULL))
366 sigdelset (&set, SIGHUP);
367 sigaddset (&set, SIGINT);
368 sigaddset (&set, SIGQUIT);
369 sigaddset (&set, SIGTERM);
371 sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
372 pthread_sigmask (SIG_BLOCK, &set, &oldset);
376 vlc_sem_init(&thread->finished, 0);
377 atomic_store(&thread->killed, false);
378 thread->killable = true;
380 thread->entry = entry;
382 vlc_mutex_init(&thread->lock);
385 pthread_attr_init (&attr);
386 pthread_attr_setdetachstate (&attr, detach ? PTHREAD_CREATE_DETACHED
387 : PTHREAD_CREATE_JOINABLE);
389 ret = pthread_create (&thread->thread, &attr,
390 detach ? detached_thread : joinable_thread, thread);
391 pthread_attr_destroy (&attr);
393 pthread_sigmask (SIG_SETMASK, &oldset, NULL);
398 int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
402 return vlc_clone_attr (th, entry, data, false);
405 void vlc_join (vlc_thread_t handle, void **result)
407 vlc_sem_wait (&handle->finished);
408 vlc_sem_destroy (&handle->finished);
410 int val = pthread_join (handle->thread, result);
411 VLC_THREAD_ASSERT ("joining thread");
412 vlc_mutex_destroy(&handle->lock);
416 int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void *data,
424 return vlc_clone_attr (th, entry, data, true);
427 int vlc_set_priority (vlc_thread_t th, int priority)
429 (void) th; (void) priority;
433 void vlc_cancel (vlc_thread_t thread_id)
435 pthread_cond_t *cond;
437 atomic_store(&thread_id->killed, true);
439 vlc_mutex_lock(&thread_id->lock);
440 cond = thread_id->cond;
442 pthread_cond_broadcast(cond);
443 vlc_mutex_unlock(&thread_id->lock);
446 int vlc_savecancel (void)
448 if (!thread) /* not created by VLC, can't be cancelled */
451 int oldstate = thread->killable;
452 thread->killable = false;
456 void vlc_restorecancel (int state)
458 if (!thread) /* not created by VLC, can't be cancelled */
461 thread->killable = state;
464 void vlc_testcancel (void)
466 if (!thread) /* not created by VLC, can't be cancelled */
468 if (!thread->killable)
470 if (!atomic_load(&thread->killed))
478 int vlc_threadvar_create (vlc_threadvar_t *key, void (*destr) (void *))
480 return pthread_key_create (key, destr);
483 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
485 pthread_key_delete (*p_tls);
488 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
490 return pthread_setspecific (key, value);
493 void *vlc_threadvar_get (vlc_threadvar_t key)
495 return pthread_getspecific (key);
503 if (unlikely(clock_gettime (CLOCK_MONOTONIC, &ts) != 0))
506 return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
510 void mwait (mtime_t deadline)
515 vlc_mutex_init (&lock);
516 vlc_cond_init (&wait);
518 vlc_mutex_lock (&lock);
519 mutex_cleanup_push (&lock);
520 while (!vlc_cond_timedwait (&wait, &lock, deadline));
523 vlc_cond_destroy (&wait);
524 vlc_mutex_destroy (&lock);
528 void msleep (mtime_t delay)
530 mwait (mdate () + delay);
535 unsigned vlc_GetCPUCount(void)
537 return sysconf(_SC_NPROCESSORS_CONF);