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 */
48 #if !defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && !defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP)
49 #error no pthread monotonic clock support
53 static struct timespec mtime_to_ts (mtime_t date)
55 lldiv_t d = lldiv (date, CLOCK_FREQ);
56 struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
62 #define vlc_assert(x) do { \
64 __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d: %s", \
65 __FILE__, __LINE__, #x \
73 vlc_thread_fatal (const char *action, int error,
74 const char *function, const char *file, unsigned line)
79 switch (strerror_r (error, buf, sizeof (buf)))
84 case ERANGE: /* should never happen */
85 msg = "unknown (too big to display)";
88 msg = "unknown (invalid error number)";
92 __android_log_print(ANDROID_LOG_ERROR, "vlc",
93 "LibVLC fatal error %s (%d) in thread %d "
94 "at %s:%u in %s\n Error message: %s\n",
95 action, error, syscall (__NR_gettid), file, line, function, msg);
100 # define VLC_THREAD_ASSERT( action ) \
102 vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
104 # define VLC_THREAD_ASSERT( action ) ((void)val)
108 void vlc_mutex_init( vlc_mutex_t *p_mutex )
110 pthread_mutexattr_t attr;
112 pthread_mutexattr_init (&attr);
114 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_DEFAULT);
116 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
118 pthread_mutex_init (p_mutex, &attr);
119 pthread_mutexattr_destroy( &attr );
122 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
124 pthread_mutexattr_t attr;
126 pthread_mutexattr_init (&attr);
127 pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
128 pthread_mutex_init (p_mutex, &attr);
129 pthread_mutexattr_destroy( &attr );
133 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
135 int val = pthread_mutex_destroy( p_mutex );
136 VLC_THREAD_ASSERT ("destroying mutex");
140 void vlc_assert_locked (vlc_mutex_t *p_mutex)
142 vlc_assert (pthread_mutex_lock (p_mutex) == EDEADLK);
146 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
148 int val = pthread_mutex_lock( p_mutex );
149 VLC_THREAD_ASSERT ("locking mutex");
152 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
154 int val = pthread_mutex_trylock( p_mutex );
157 VLC_THREAD_ASSERT ("locking mutex");
161 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
163 int val = pthread_mutex_unlock( p_mutex );
164 VLC_THREAD_ASSERT ("unlocking mutex");
170 pthread_cond_t *cond; /// Non-null if thread waiting on cond
171 vlc_mutex_t lock ; /// Protects cond
174 void *(*entry)(void*);
181 static __thread struct vlc_thread *thread = NULL;
183 void vlc_threads_setup (libvlc_int_t *p_libvlc)
190 void vlc_cond_init (vlc_cond_t *condvar)
192 #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
193 if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
196 pthread_condattr_t attr;
198 pthread_condattr_init (&attr);
199 pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
201 if (unlikely(pthread_cond_init (&condvar->cond, &attr)))
204 condvar->clock = CLOCK_MONOTONIC;
207 void vlc_cond_init_daytime (vlc_cond_t *condvar)
209 if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
211 condvar->clock = CLOCK_REALTIME;
214 void vlc_cond_destroy (vlc_cond_t *condvar)
216 int val = pthread_cond_destroy (&condvar->cond);
217 VLC_THREAD_ASSERT ("destroying condition");
220 void vlc_cond_signal (vlc_cond_t *condvar)
222 int val = pthread_cond_signal (&condvar->cond);
223 VLC_THREAD_ASSERT ("signaling condition variable");
226 void vlc_cond_broadcast (vlc_cond_t *condvar)
228 pthread_cond_broadcast (&condvar->cond);
231 void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
233 vlc_thread_t th = thread;
238 if (vlc_mutex_trylock (&th->lock) == 0)
240 th->cond = &condvar->cond;
241 vlc_mutex_unlock (&th->lock);
244 { /* The lock is already held by another thread.
245 * => That other thread has just cancelled this one. */
247 /* Cancellation did not occur even though this thread is cancelled.
248 * => Cancellation is disabled. */
253 int val = pthread_cond_wait (&condvar->cond, p_mutex);
254 VLC_THREAD_ASSERT ("waiting on condition");
258 if (vlc_mutex_trylock (&th->lock) == 0)
261 vlc_mutex_unlock (&th->lock);
263 /* Else: This thread was cancelled and is cancellable.
264 vlc_testcancel() will take of it right there: */
269 int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
272 struct timespec ts = mtime_to_ts (deadline);
273 vlc_thread_t th = thread;
274 #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
275 int (*cb)(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
281 if (vlc_mutex_trylock (&th->lock) == 0)
283 th->cond = &condvar->cond;
284 vlc_mutex_unlock (&th->lock);
287 { /* The lock is already held by another thread.
288 * => That other thread has just cancelled this one. */
290 /* Cancellation did not occur even though this thread is cancelled.
291 * => Cancellation is disabled. */
296 #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
297 switch (condvar->clock)
300 cb = pthread_cond_timedwait;
302 case CLOCK_MONOTONIC:
303 cb = pthread_cond_timedwait_monotonic_np;
306 vlc_assert_unreachable ();
309 int val = cb (&condvar->cond, p_mutex, &ts);
311 int val = pthread_cond_timedwait(&condvar->cond, p_mutex, &ts);
314 if (val != ETIMEDOUT)
315 VLC_THREAD_ASSERT ("timed-waiting on condition");
319 if (vlc_mutex_trylock (&th->lock) == 0)
322 vlc_mutex_unlock (&th->lock);
324 /* Else: This thread was cancelled and is cancellable.
325 vlc_testcancel() will take of it right there: */
332 static void clean_detached_thread(void *data)
334 struct vlc_thread *thread = data;
336 /* release thread handle */
337 vlc_mutex_destroy(&thread->lock);
341 static void *detached_thread(void *data)
343 vlc_thread_t th = data;
347 vlc_cleanup_push(clean_detached_thread, data);
354 static void finish_joinable_thread(void *data)
356 vlc_thread_t th = data;
358 vlc_sem_post(&th->finished);
361 static void *joinable_thread(void *data)
363 vlc_thread_t th = data;
366 vlc_cleanup_push(finish_joinable_thread, th);
368 ret = th->entry(th->data);
374 static int vlc_clone_attr (vlc_thread_t *th, void *(*entry) (void *),
375 void *data, bool detach)
377 vlc_thread_t thread = malloc (sizeof (*thread));
378 if (unlikely(thread == NULL))
387 sigdelset (&set, SIGHUP);
388 sigaddset (&set, SIGINT);
389 sigaddset (&set, SIGQUIT);
390 sigaddset (&set, SIGTERM);
392 sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
393 pthread_sigmask (SIG_BLOCK, &set, &oldset);
397 vlc_sem_init(&thread->finished, 0);
398 atomic_store(&thread->killed, false);
399 thread->killable = true;
401 thread->entry = entry;
403 vlc_mutex_init(&thread->lock);
406 pthread_attr_init (&attr);
407 pthread_attr_setdetachstate (&attr, detach ? PTHREAD_CREATE_DETACHED
408 : PTHREAD_CREATE_JOINABLE);
410 ret = pthread_create (&thread->thread, &attr,
411 detach ? detached_thread : joinable_thread, thread);
412 pthread_attr_destroy (&attr);
414 pthread_sigmask (SIG_SETMASK, &oldset, NULL);
419 int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
423 return vlc_clone_attr (th, entry, data, false);
426 void vlc_join (vlc_thread_t handle, void **result)
428 vlc_sem_wait (&handle->finished);
429 vlc_sem_destroy (&handle->finished);
431 int val = pthread_join (handle->thread, result);
432 VLC_THREAD_ASSERT ("joining thread");
433 vlc_mutex_destroy(&handle->lock);
437 int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void *data,
445 return vlc_clone_attr (th, entry, data, true);
448 int vlc_set_priority (vlc_thread_t th, int priority)
450 (void) th; (void) priority;
454 void vlc_cancel (vlc_thread_t thread_id)
456 pthread_cond_t *cond;
458 atomic_store(&thread_id->killed, true);
460 vlc_mutex_lock(&thread_id->lock);
461 cond = thread_id->cond;
463 pthread_cond_broadcast(cond);
464 vlc_mutex_unlock(&thread_id->lock);
467 int vlc_savecancel (void)
469 if (!thread) /* not created by VLC, can't be cancelled */
472 int oldstate = thread->killable;
473 thread->killable = false;
477 void vlc_restorecancel (int state)
479 if (!thread) /* not created by VLC, can't be cancelled */
482 thread->killable = state;
485 void vlc_testcancel (void)
487 if (!thread) /* not created by VLC, can't be cancelled */
489 if (!thread->killable)
491 if (!atomic_load(&thread->killed))
499 int vlc_threadvar_create (vlc_threadvar_t *key, void (*destr) (void *))
501 return pthread_key_create (key, destr);
504 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
506 pthread_key_delete (*p_tls);
509 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
511 return pthread_setspecific (key, value);
514 void *vlc_threadvar_get (vlc_threadvar_t key)
516 return pthread_getspecific (key);
524 if (unlikely(clock_gettime (CLOCK_MONOTONIC, &ts) != 0))
527 return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
531 void mwait (mtime_t deadline)
536 vlc_mutex_init (&lock);
537 vlc_cond_init (&wait);
539 vlc_mutex_lock (&lock);
540 mutex_cleanup_push (&lock);
541 while (!vlc_cond_timedwait (&wait, &lock, deadline));
544 vlc_cond_destroy (&wait);
545 vlc_mutex_destroy (&lock);
549 void msleep (mtime_t delay)
551 mwait (mdate () + delay);
556 unsigned vlc_GetCPUCount(void)
558 return sysconf(_SC_NPROCESSORS_CONF);