]> git.sesse.net Git - vlc/blob - src/android/thread.c
Remove the deprecated vlc_atomic_t type.
[vlc] / src / android / thread.c
1 /*****************************************************************************
2  * thread.c : android pthread back-end for LibVLC
3  *****************************************************************************
4  * Copyright (C) 1999-2012 VLC authors and VideoLAN
5  *
6  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
7  *          Samuel Hocevar <sam@zoy.org>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *          Clément Sténac
10  *          Rémi Denis-Courmont
11  *
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.
16  *
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.
21  *
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  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_atomic.h>
33
34 #include "libvlc.h"
35 #include <signal.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <assert.h>
39
40 #include <sys/types.h>
41 #include <unistd.h> /* fsync() */
42 #include <pthread.h>
43 #include <sched.h>
44
45 #include <android/log.h>
46 #include <sys/syscall.h> /* __NR_gettid */
47
48 /* helper */
49 static struct timespec mtime_to_ts (mtime_t date)
50 {
51     lldiv_t d = lldiv (date, CLOCK_FREQ);
52     struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
53
54     return ts;
55 }
56
57 /* debug */
58 #define vlc_assert(x) do { \
59     if (unlikely(!x)) { \
60     __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d: %s", \
61         __FILE__, __LINE__, #x \
62         ); \
63         abort(); \
64     } \
65 } while(0)
66
67 #ifndef NDEBUG
68 static void
69 vlc_thread_fatal (const char *action, int error,
70                   const char *function, const char *file, unsigned line)
71 {
72     char buf[1000];
73     const char *msg;
74
75     switch (strerror_r (error, buf, sizeof (buf)))
76     {
77         case 0:
78             msg = buf;
79             break;
80         case ERANGE: /* should never happen */
81             msg = "unknown (too big to display)";
82             break;
83         default:
84             msg = "unknown (invalid error number)";
85             break;
86     }
87
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);
92
93     abort ();
94 }
95
96 # define VLC_THREAD_ASSERT( action ) \
97     if (unlikely(val)) \
98         vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
99 #else
100 # define VLC_THREAD_ASSERT( action ) ((void)val)
101 #endif
102
103 /* mutexes */
104 void vlc_mutex_init( vlc_mutex_t *p_mutex )
105 {
106     pthread_mutexattr_t attr;
107
108     pthread_mutexattr_init (&attr);
109 #ifdef NDEBUG
110     pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_DEFAULT);
111 #else
112     pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
113 #endif
114     pthread_mutex_init (p_mutex, &attr);
115     pthread_mutexattr_destroy( &attr );
116 }
117
118 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
119 {
120     pthread_mutexattr_t attr;
121
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 );
126 }
127
128
129 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
130 {
131     int val = pthread_mutex_destroy( p_mutex );
132     VLC_THREAD_ASSERT ("destroying mutex");
133 }
134
135 #ifndef NDEBUG
136 void vlc_assert_locked (vlc_mutex_t *p_mutex)
137 {
138     vlc_assert (pthread_mutex_lock (p_mutex) == EDEADLK);
139 }
140 #endif
141
142 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
143 {
144     int val = pthread_mutex_lock( p_mutex );
145     VLC_THREAD_ASSERT ("locking mutex");
146 }
147
148 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
149 {
150     int val = pthread_mutex_trylock( p_mutex );
151
152     if (val != EBUSY)
153         VLC_THREAD_ASSERT ("locking mutex");
154     return val;
155 }
156
157 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
158 {
159     int val = pthread_mutex_unlock( p_mutex );
160     VLC_THREAD_ASSERT ("unlocking mutex");
161 }
162
163 struct vlc_thread
164 {
165     pthread_t      thread;
166     pthread_cond_t *cond; /// Non-null if thread waiting on cond
167     vlc_mutex_t    lock ; /// Protects cond
168     vlc_sem_t      finished;
169
170     void *(*entry)(void*);
171     void *data;
172
173     atomic_bool killed;
174     bool killable;
175 };
176
177 static __thread struct vlc_thread *thread = NULL;
178
179 void vlc_threads_setup (libvlc_int_t *p_libvlc)
180 {
181     (void)p_libvlc;
182 }
183
184 /* cond */
185
186 void vlc_cond_init (vlc_cond_t *condvar)
187 {
188     if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
189         abort ();
190     condvar->clock = CLOCK_MONOTONIC;
191 }
192
193 void vlc_cond_init_daytime (vlc_cond_t *condvar)
194 {
195     if (unlikely(pthread_cond_init (&condvar->cond, NULL)))
196         abort ();
197     condvar->clock = CLOCK_REALTIME;
198 }
199
200 void vlc_cond_destroy (vlc_cond_t *condvar)
201 {
202     int val = pthread_cond_destroy (&condvar->cond);
203     VLC_THREAD_ASSERT ("destroying condition");
204 }
205
206 void vlc_cond_signal (vlc_cond_t *condvar)
207 {
208     int val = pthread_cond_signal (&condvar->cond);
209     VLC_THREAD_ASSERT ("signaling condition variable");
210 }
211
212 void vlc_cond_broadcast (vlc_cond_t *condvar)
213 {
214     pthread_cond_broadcast (&condvar->cond);
215 }
216
217 void vlc_cond_wait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex)
218 {
219     vlc_thread_t th = thread;
220
221     if (th != NULL)
222     {
223         vlc_testcancel ();
224         if (vlc_mutex_trylock (&th->lock) == 0)
225         {
226             th->cond = &condvar->cond;
227             vlc_mutex_unlock (&th->lock);
228         }
229         else
230         {   /* The lock is already held by another thread.
231              * => That other thread has just cancelled this one. */
232             vlc_testcancel ();
233             /* Cancellation did not occur even though this thread is cancelled.
234              * => Cancellation is disabled. */
235             th = NULL;
236         }
237     }
238
239     int val = pthread_cond_wait (&condvar->cond, p_mutex);
240     VLC_THREAD_ASSERT ("waiting on condition");
241
242     if (th != NULL)
243     {
244         if (vlc_mutex_trylock (&th->lock) == 0)
245         {
246             thread->cond = NULL;
247             vlc_mutex_unlock (&th->lock);
248         }
249         /* Else: This thread was cancelled and is cancellable.
250                  vlc_testcancel() will take of it right there: */
251         vlc_testcancel();
252     }
253 }
254
255 int vlc_cond_timedwait (vlc_cond_t *condvar, vlc_mutex_t *p_mutex,
256                         mtime_t deadline)
257 {
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 *);
261
262     if (th != NULL)
263     {
264         vlc_testcancel ();
265         if (vlc_mutex_trylock (&th->lock) == 0)
266         {
267             th->cond = &condvar->cond;
268             vlc_mutex_unlock (&th->lock);
269         }
270         else
271         {   /* The lock is already held by another thread.
272              * => That other thread has just cancelled this one. */
273             vlc_testcancel ();
274             /* Cancellation did not occur even though this thread is cancelled.
275              * => Cancellation is disabled. */
276             th = NULL;
277         }
278     }
279
280     switch (condvar->clock)
281     {
282          case CLOCK_REALTIME:
283              cb = pthread_cond_timedwait;
284              break;
285          case CLOCK_MONOTONIC:
286              cb = pthread_cond_timedwait_monotonic_np;
287              break;
288          default:
289              assert (0);
290     }
291
292     int val = cb (&condvar->cond, p_mutex, &ts);
293     if (val != ETIMEDOUT)
294         VLC_THREAD_ASSERT ("timed-waiting on condition");
295
296     if (th != NULL)
297     {
298         if (vlc_mutex_trylock (&th->lock) == 0)
299         {
300             thread->cond = NULL;
301             vlc_mutex_unlock (&th->lock);
302         }
303         /* Else: This thread was cancelled and is cancellable.
304                  vlc_testcancel() will take of it right there: */
305         vlc_testcancel();
306     }
307     return val;
308 }
309
310 /* pthread */
311 static void clean_detached_thread(void *data)
312 {
313     struct vlc_thread *thread = data;
314
315     /* release thread handle */
316     vlc_mutex_destroy(&thread->lock);
317     free(thread);
318 }
319
320 static void *detached_thread(void *data)
321 {
322     vlc_thread_t th = data;
323
324     thread = th;
325
326     vlc_cleanup_push(clean_detached_thread, data);
327     th->entry(th->data);
328     vlc_cleanup_run();
329
330     return NULL;
331 }
332
333 static void finish_joinable_thread(void *data)
334 {
335     vlc_thread_t th = data;
336
337     vlc_sem_post(&th->finished);
338 }
339
340 static void *joinable_thread(void *data)
341 {
342     vlc_thread_t th = data;
343     void *ret;
344
345     vlc_cleanup_push(finish_joinable_thread, th);
346     thread = th;
347     ret = th->entry(th->data);
348     vlc_cleanup_run();
349
350     return ret;
351 }
352
353 static int vlc_clone_attr (vlc_thread_t *th, void *(*entry) (void *),
354                            void *data, bool detach)
355 {
356     vlc_thread_t thread = malloc (sizeof (*thread));
357     if (unlikely(thread == NULL))
358         return ENOMEM;
359
360     int ret;
361
362     sigset_t oldset;
363     {
364         sigset_t set;
365         sigemptyset (&set);
366         sigdelset (&set, SIGHUP);
367         sigaddset (&set, SIGINT);
368         sigaddset (&set, SIGQUIT);
369         sigaddset (&set, SIGTERM);
370
371         sigaddset (&set, SIGPIPE); /* We don't want this one, really! */
372         pthread_sigmask (SIG_BLOCK, &set, &oldset);
373     }
374
375
376     vlc_sem_init(&thread->finished, 0);
377     atomic_store(&thread->killed, false);
378     thread->killable = true;
379     thread->cond = NULL;
380     thread->entry = entry;
381     thread->data = data;
382     vlc_mutex_init(&thread->lock);
383
384     pthread_attr_t attr;
385     pthread_attr_init (&attr);
386     pthread_attr_setdetachstate (&attr, detach ? PTHREAD_CREATE_DETACHED
387                                                : PTHREAD_CREATE_JOINABLE);
388
389     ret = pthread_create (&thread->thread, &attr,
390                           detach ? detached_thread : joinable_thread, thread);
391     pthread_attr_destroy (&attr);
392
393     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
394     *th = thread;
395     return ret;
396 }
397
398 int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
399                int priority)
400 {
401     (void) priority;
402     return vlc_clone_attr (th, entry, data, false);
403 }
404
405 void vlc_join (vlc_thread_t handle, void **result)
406 {
407     vlc_sem_wait (&handle->finished);
408     vlc_sem_destroy (&handle->finished);
409
410     int val = pthread_join (handle->thread, result);
411     VLC_THREAD_ASSERT ("joining thread");
412     vlc_mutex_destroy(&handle->lock);
413     free(handle);
414 }
415
416 int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void *data,
417                       int priority)
418 {
419     vlc_thread_t dummy;
420     if (th == NULL)
421         th = &dummy;
422
423     (void) priority;
424     return vlc_clone_attr (th, entry, data, true);
425 }
426
427 int vlc_set_priority (vlc_thread_t th, int priority)
428 {
429     (void) th; (void) priority;
430     return VLC_SUCCESS;
431 }
432
433 void vlc_cancel (vlc_thread_t thread_id)
434 {
435     pthread_cond_t *cond;
436
437     atomic_store(&thread_id->killed, true);
438
439     vlc_mutex_lock(&thread_id->lock);
440     cond = thread_id->cond;
441     if (cond)
442         pthread_cond_broadcast(cond);
443     vlc_mutex_unlock(&thread_id->lock);
444 }
445
446 int vlc_savecancel (void)
447 {
448     if (!thread) /* not created by VLC, can't be cancelled */
449         return true;
450
451     int oldstate = thread->killable;
452     thread->killable = false;
453     return oldstate;
454 }
455
456 void vlc_restorecancel (int state)
457 {
458     if (!thread) /* not created by VLC, can't be cancelled */
459         return;
460
461     thread->killable = state;
462 }
463
464 void vlc_testcancel (void)
465 {
466     if (!thread) /* not created by VLC, can't be cancelled */
467         return;
468     if (!thread->killable)
469         return;
470     if (!atomic_load(&thread->killed))
471         return;
472
473     pthread_exit(NULL);
474 }
475
476 /* threadvar */
477
478 int vlc_threadvar_create (vlc_threadvar_t *key, void (*destr) (void *))
479 {
480     return pthread_key_create (key, destr);
481 }
482
483 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
484 {
485     pthread_key_delete (*p_tls);
486 }
487
488 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
489 {
490     return pthread_setspecific (key, value);
491 }
492
493 void *vlc_threadvar_get (vlc_threadvar_t key)
494 {
495     return pthread_getspecific (key);
496 }
497
498 /* time */
499 mtime_t mdate (void)
500 {
501     struct timespec ts;
502
503     if (unlikely(clock_gettime (CLOCK_MONOTONIC, &ts) != 0))
504         abort ();
505
506     return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
507 }
508
509 #undef mwait
510 void mwait (mtime_t deadline)
511 {
512     vlc_mutex_t lock;
513     vlc_cond_t wait;
514
515     vlc_mutex_init (&lock);
516     vlc_cond_init (&wait);
517
518     vlc_mutex_lock (&lock);
519     mutex_cleanup_push (&lock);
520     while (!vlc_cond_timedwait (&wait, &lock, deadline));
521     vlc_cleanup_run ();
522
523     vlc_cond_destroy (&wait);
524     vlc_mutex_destroy (&lock);
525 }
526
527 #undef msleep
528 void msleep (mtime_t delay)
529 {
530     mwait (mdate () + delay);
531 }
532
533 /* cpu */
534
535 unsigned vlc_GetCPUCount(void)
536 {
537     return sysconf(_SC_NPROCESSORS_CONF);
538 }