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
);