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