0001-android-threads-support.patch 14.5 KB
Newer Older
1
From ec67d929b63cdebc2af0f415ab0a54c6a3329816 Mon Sep 17 00:00:00 2001
2
3
From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= <funman@videolan.org>
Date: Sat, 10 Mar 2012 04:54:23 -0500
Rafaël Carré's avatar
Rafaël Carré committed
4
Subject: [PATCH 1/3] android: threads support
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

emulate pthread_cancel (based on win32 code)
TODO:
    - move initialization somewhere else

    - use lock for cond/killed/killable?
      -> ndk gcc doesn't support atomic ops nor __thread
      -> read/writes should be atomic on arm (ldr/str)
      -> cond atomic access should be guaranteed by using the cond's
      mutex when signaling

    - split thread.c to move specific code to own file:
    affected functions are:
     * vlc_cond_wait()
     * vlc_cond_timedwait()
     * vlc_clone_attr()
     * vlc_join()
     * vlc_cancel()
     * vlc_savecancel()
     * vlc_testcancel()
     * vlc_restorecancel()
     * vlc_control_cancel()
27
28
     * vlc_sem_wait()
     * msleep()
29

30
timer, rwlocks, mutexes, clock, threadvar
31
32
33
are 100% shared with linux so it'd be useless to have 2 copies.
---
 include/vlc_threads.h |   13 +++
Rafaël Carré's avatar
Rafaël Carré committed
34
 lib/error.c           |   10 ++
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
35
 src/Makefile.am       |   16 ++++
36
 src/posix/thread.c    |  245 +++++++++++++++++++++++++++++++++++++++++++------
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
37
 4 files changed, 256 insertions(+), 28 deletions(-)
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index ebf94e2..dad50b1 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -43,6 +43,15 @@
 
 #   define pthread_sigmask  sigprocmask
 
+#elif defined( __ANDROID__ )      /* pthreads without pthread_cancel() */
+
+#   define LIBVLC_USE_PTHREAD 1
+
+#   include <unistd.h> /* _POSIX_SPIN_LOCKS */
+#   include <pthread.h>
+#   include <poll.h>
+#   include <semaphore.h>
+
 #else                                         /* pthreads (like Linux & BSD) */
 #   define LIBVLC_USE_PTHREAD 1
 #   define LIBVLC_USE_PTHREAD_CANCEL 1
@@ -118,7 +127,11 @@
  *****************************************************************************/
 
 #if defined (LIBVLC_USE_PTHREAD)
+# ifdef LIBVLC_USE_PTHREAD_CANCEL
 typedef pthread_t       vlc_thread_t;
+# else
+typedef struct vlc_thread *vlc_thread_t;
+# endif
 typedef pthread_mutex_t vlc_mutex_t;
 #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
 typedef pthread_cond_t  vlc_cond_t;
diff --git a/lib/error.c b/lib/error.c
Rafaël Carré's avatar
Rafaël Carré committed
72
index d053faa..52840e8 100644
73
74
--- a/lib/error.c
+++ b/lib/error.c
Rafaël Carré's avatar
Rafaël Carré committed
75
76
77
@@ -33,11 +33,17 @@ static vlc_threadvar_t context;
 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
 static uintptr_t refs = 0;
78
 
Rafaël Carré's avatar
Rafaël Carré committed
79
+void andro_init_threads(bool);
80
+
Rafaël Carré's avatar
Rafaël Carré committed
81
82
 void libvlc_threads_init (void)
 {
83
     vlc_mutex_lock (&lock);
Rafaël Carré's avatar
Rafaël Carré committed
84
     if (refs++ == 0)
85
86
87
     {
+#ifdef __ANDROID__
+            /* XXX: move somewhere else? */
Rafaël Carré's avatar
Rafaël Carré committed
88
+            andro_init_threads(true);
89
+#endif
Rafaël Carré's avatar
Rafaël Carré committed
90
91
         vlc_threadvar_create (&context, free);
         libvlc_log_init ();
92
     }
Rafaël Carré's avatar
Rafaël Carré committed
93
@@ -52,6 +58,10 @@ void libvlc_threads_deinit (void)
94
     {
Rafaël Carré's avatar
Rafaël Carré committed
95
96
         libvlc_log_deinit ();
         vlc_threadvar_delete (&context);
97
98
+#ifdef __ANDROID__
+            /* XXX: move somewhere else? */
Rafaël Carré's avatar
Rafaël Carré committed
99
+            andro_init_threads(false);
100
101
102
103
104
+#endif
     }
     vlc_mutex_unlock (&lock);
 }
diff --git a/src/Makefile.am b/src/Makefile.am
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
105
index d6d083e..4cf4aba 100644
106
107
--- a/src/Makefile.am
+++ b/src/Makefile.am
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
108
@@ -195,6 +195,7 @@ libvlc_win32_rc.$(OBJEXT): libvlc_win32_rc.rc
109
110
111
112
113
114
115
 EXTRA_libvlccore_la_SOURCES = \
 	$(SOURCES_libvlc_darwin) \
 	$(SOURCES_libvlc_linux) \
+	$(SOURCES_libvlc_android) \
 	$(SOURCES_libvlc_win32) \
 	$(SOURCES_libvlc_os2) \
 	$(SOURCES_libvlc_other) \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
116
@@ -205,6 +206,9 @@ EXTRA_libvlccore_la_SOURCES = \
117
118
119
120
121
122
123
124
125
 if HAVE_DARWIN
 libvlccore_la_SOURCES += $(SOURCES_libvlc_darwin)
 else
+if HAVE_ANDROID
+libvlccore_la_SOURCES += $(SOURCES_libvlc_android)
+else
 if HAVE_LINUX
 libvlccore_la_SOURCES += $(SOURCES_libvlc_linux)
 else
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
126
@@ -227,6 +231,7 @@ endif
127
128
129
130
131
132
133
 endif
 endif
 endif
+endif
 if BUILD_HTTPD
 libvlccore_la_SOURCES += $(SOURCES_libvlc_httpd)
 endif
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
134
@@ -246,6 +251,17 @@ SOURCES_libvlc_darwin = \
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 	posix/rand.c \
 	$(NULL)
 
+SOURCES_libvlc_android = \
+	posix/dirs.c \
+	posix/filesystem.c \
+	posix/plugin.c \
+	posix/thread.c \
+	posix/linux_cpu.c \
+	posix/linux_specific.c \
+	posix/specific.c \
+	posix/rand.c \
+	$(NULL)
+
 SOURCES_libvlc_linux = \
 	posix/dirs.c \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
151
 	posix/filesystem.c \
152
diff --git a/src/posix/thread.c b/src/posix/thread.c
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
153
index 138eac1..a208da0e 100644
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
--- a/src/posix/thread.c
+++ b/src/posix/thread.c
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * thread.c : pthread back-end for LibVLC
+ * thread.c : android pthread back-end for LibVLC
  *****************************************************************************
  * Copyright (C) 1999-2009 VLC authors and VideoLAN
  *
@@ -43,6 +43,8 @@
 #include <sched.h>
 #include <sys/time.h> /* gettimeofday() */
 
+# include <android/log.h>
+
 #ifdef __linux__
 # include <sys/syscall.h> /* SYS_gettid */
 #endif
@@ -73,6 +75,17 @@
 # define _POSIX_MONOTONIC_CLOCK (-1)
 #endif
 
+
+#undef assert
+#define assert(x) do { \
+    if (!x) { \
+    __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d: %s", \
+        __FILE__, __LINE__, #x \
+        ); \
+        abort(); \
+    } \
+} while(0)
+
 #if (_POSIX_TIMERS > 0)
 static unsigned vlc_clock_prec;
 
@@ -146,10 +159,11 @@ void vlc_trace (const char *fn, const char *file, unsigned line)
 
 static inline unsigned long vlc_threadid (void)
 {
-#if defined (__linux__)
+#if defined (__ANDROID__)
+     return syscall (__NR_gettid);
+#elif defined (__linux__)
      /* glibc does not provide a call for this */
      return syscall (SYS_gettid);
-
 #else
      union { pthread_t th; unsigned long int i; } v = { };
      v.th = pthread_self ();
@@ -169,7 +183,7 @@ vlc_thread_fatal (const char *action, int error,
                   const char *function, const char *file, unsigned line)
 {
     int canc = vlc_savecancel ();
-    fprintf (stderr, "LibVLC fatal error %s (%d) in thread %lu ",
+    __android_log_print(ANDROID_LOG_ERROR, "vlc", "LibVLC fatal error %s (%d) in thread %lu ",
              action, error, vlc_threadid ());
     vlc_trace (function, file, line);
 
213
@@ -335,6 +349,57 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
     VLC_THREAD_ASSERT ("unlocking mutex");
 }
 
+struct vlc_thread
+{
+    pthread_t      thread;
+    pthread_cond_t *cond; /// Non-null if thread waiting on cond
+    pthread_mutex_t *lock ; /// Non-null if thread waiting on cond
+    vlc_cleanup_t *cleaners;
+
+    void *(*entry)(void*);
+    void *data;
+
+    bool killable;
+    bool killed;
229
+    bool finished;
230
231
232
233
234
235
236
237
238
239
240
241
242
+};
+
+static pthread_key_t thread_key = 0;
+
+/* XXX: move somewhere else? */
+void andro_init_threads(bool init)
+{
+    static struct vlc_thread main_thread = {
+        .cond     = NULL,
+        .lock     = NULL,
+        .cleaners = NULL,
+        .killable = false,
+        .killed   = false,
243
+        .finished = false,
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
+        .entry    = NULL,
+        .data     = NULL,
+    };
+
+    if (init) {
+        main_thread.thread = pthread_self();
+        pthread_key_create(&thread_key, NULL);
+        if (pthread_setspecific(thread_key, &main_thread))
+            abort();
+    } else {
+        pthread_key_delete(thread_key);
+    }
+}
+
+static void *andro_Thread(void *data)
+{
+    vlc_thread_t thread = data;
+    if (pthread_setspecific(thread_key, thread))
+        abort();
263
264
265
+    void *ret = thread->entry(thread->data);
+    thread->finished = true;
+    return ret;
266
267
268
269
270
+}
+
 /**
  * Initializes a condition variable.
  */
271
@@ -428,7 +493,22 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  */
 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
 {
+    vlc_thread_t thread = pthread_getspecific(thread_key);
+
+    if (thread) {
+        vlc_testcancel();
+        thread->cond = p_condvar;
+        thread->lock = p_mutex;
+    }
+
     int val = pthread_cond_wait( p_condvar, p_mutex );
+
+    if (thread) {
+        thread->cond = NULL;
+        thread->lock = NULL;
+        vlc_testcancel();
+    }
+
     VLC_THREAD_ASSERT ("waiting on condition");
 }
 
294
@@ -450,10 +530,25 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
                         mtime_t deadline)
 {
+    vlc_thread_t thread = pthread_getspecific(thread_key);
     struct timespec ts = mtime_to_ts (deadline);
+
+    if (thread) {
+        vlc_testcancel();
+        thread->cond = p_condvar;
+        thread->lock = p_mutex;
+    }
+
     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
     if (val != ETIMEDOUT)
         VLC_THREAD_ASSERT ("timed-waiting on condition");
+
+    if (thread) {
+        thread->cond = NULL;
+        thread->lock = NULL;
+        vlc_testcancel();
+    }
+
     return val;
 }
 
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
@@ -532,10 +627,14 @@ void vlc_sem_wait (vlc_sem_t *sem)
 
     val = EINVAL;
 #else
-    do
-        if (likely(sem_wait (sem) == 0))
+    do {
+        vlc_testcancel();
+        struct timespec t = mtime_to_ts (mdate());
+        t.tv_nsec += 10 * 1000 * 1000;
+        if (likely(sem_timedwait (sem, &t) == 0))
             return;
-    while ((val = errno) == EINTR);
+        val = errno;
+    } while (val == EINTR || val == ETIMEDOUT);
 #endif
 
     VLC_THREAD_ASSERT ("locking semaphore");
@@ -720,7 +819,22 @@ static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
339
340
341
342
343
344
345
346
347
348
     assert (ret == 0); /* fails iif VLC_STACKSIZE is invalid */
 #endif
 
-    ret = pthread_create (th, attr, entry, data);
+    vlc_thread_t thread = malloc (sizeof (*thread));
+    if (unlikely(thread == NULL))
+        return ENOMEM;
+
+    thread->killable = true;
+    thread->killed = false;
349
+    thread->finished = false,
350
351
352
353
354
355
356
357
358
359
360
361
+    thread->cond = NULL;
+    thread->lock = NULL;
+    thread->cleaners = NULL;
+    thread->entry = entry;
+    thread->data = data;
+
+    *th = thread;
+    ret = pthread_create (&thread->thread, attr, andro_Thread, thread);
+
     pthread_sigmask (SIG_SETMASK, &oldset, NULL);
     pthread_attr_destroy (attr);
     return ret;
362
@@ -761,8 +875,14 @@ int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
363
364
365
366
  */
 void vlc_join (vlc_thread_t handle, void **result)
 {
-    int val = pthread_join (handle, result);
367
368
369
370
371
+    do {
+        vlc_testcancel();
+        msleep(CLOCK_FREQ / 100);
+    } while (!handle->finished);
+
372
373
374
375
376
377
+    int val = pthread_join (handle->thread, result);
     VLC_THREAD_ASSERT ("joining thread");
+    free(handle);
 }
 
 /**
378
@@ -828,6 +948,7 @@ int vlc_set_priority (vlc_thread_t th, int priority)
379
380
381
382
383
384
385
             return VLC_EGENERIC;
     }
 #else
+    (void) th;
     (void) priority;
 #endif
     return VLC_SUCCESS;
386
@@ -842,10 +963,25 @@ int vlc_set_priority (vlc_thread_t th, int priority)
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
  */
 void vlc_cancel (vlc_thread_t thread_id)
 {
-    pthread_cancel (thread_id);
-#ifdef HAVE_MAEMO
-    pthread_kill (thread_id, SIGRTMIN);
-#endif
+    bool self = thread_id == pthread_getspecific(thread_key);
+
+    thread_id->killed = true;
+    if (!thread_id->killable)
+        return;
+
+    vlc_mutex_t *lock = thread_id->lock;
+
+    if (lock) {
+        if (!self)
+            vlc_mutex_lock(lock);
+        if (thread_id->cond)
+            pthread_cond_broadcast(thread_id->cond);
+        if (!self)
+            vlc_mutex_unlock(lock);
+    }
+
+    if (self)
+        vlc_testcancel();
 }
 
 /**
416
@@ -858,11 +994,13 @@ void vlc_cancel (vlc_thread_t thread_id)
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  */
 int vlc_savecancel (void)
 {
-    int state;
-    int val = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
+    vlc_thread_t thread = pthread_getspecific(thread_key);
+    if (!thread) /* not created by VLC, can't be cancelled */
+        return true;
 
-    VLC_THREAD_ASSERT ("saving cancellation");
-    return state;
+    int oldstate = thread->killable;
+    thread->killable = false;
+    return oldstate;
 }
 
 /**
434
@@ -872,18 +1010,19 @@ int vlc_savecancel (void)
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  */
 void vlc_restorecancel (int state)
 {
+    vlc_thread_t thread = pthread_getspecific(thread_key);
+    if (!thread) /* not created by VLC, can't be cancelled */
+        return;
 #ifndef NDEBUG
-    int oldstate, val;
+    int oldstate = thread->killable;
 
-    val = pthread_setcancelstate (state, &oldstate);
-    /* This should fail if an invalid value for given for state */
-    VLC_THREAD_ASSERT ("restoring cancellation");
+    thread->killable = state;
 
-    if (unlikely(oldstate != PTHREAD_CANCEL_DISABLE))
+    if (unlikely(oldstate != false))
          vlc_thread_fatal ("restoring cancellation while not disabled", EINVAL,
                            __func__, __FILE__, __LINE__);
 #else
-    pthread_setcancelstate (state, NULL);
+    thread->killable = state;
 #endif
 }
 
460
@@ -896,13 +1035,48 @@ void vlc_restorecancel (int state)
461
462
463
464
465
466
467
468
469
470
471
472
473
  */
 void vlc_testcancel (void)
 {
-    pthread_testcancel ();
+    vlc_thread_t thread = pthread_getspecific(thread_key);
+    if (!thread) /* not created by VLC, can't be cancelled */
+        return;
+    if (!thread->killable || !thread->killed)
+        return;
+
+    for (vlc_cleanup_t *p = thread->cleaners; p != NULL; p = p->next)
+        p->proc (p->data);
+
474
+    thread->finished = true;
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
+    pthread_exit(NULL);
 }
 
 void vlc_control_cancel (int cmd, ...)
 {
-    (void) cmd;
-    assert (0);
+    vlc_thread_t thread = pthread_getspecific(thread_key);
+    if (!thread) /* not created by VLC, can't be cancelled */
+        return;
+    /* NOTE: This function only modifies thread-specific data, so there is no
+     * need to lock anything. */
+    va_list ap;
+
+    va_start (ap, cmd);
+    switch (cmd)
+    {
+        case VLC_CLEANUP_PUSH:
+        {
+            /* cleaner is a pointer to the caller stack, no need to allocate
+             * and copy anything. As a nice side effect, this cannot fail. */
+            vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
+            cleaner->next = thread->cleaners;
+            thread->cleaners = cleaner;
+            break;
+        }
+
+        case VLC_CLEANUP_POP:
+        {
+            thread->cleaners = thread->cleaners->next;
+            break;
+        }
+    }
+    va_end (ap);
 }
 
 /**
512
513
514
515
516
517
518
519
520
@@ -979,8 +1153,23 @@ void msleep (mtime_t delay)
     while (clock_nanosleep (vlc_clock_id, 0, &ts, &ts) == EINTR);
 
 #else
-    while (nanosleep (&ts, &ts) == -1)
-        assert (errno == EINTR);
+    vlc_testcancel();
+    for (;;) {
+        struct timespec t = { 0, 10 * 1000 * 1000 };
521
+        if (ts.tv_sec <= 0 && t.tv_nsec > ts.tv_nsec)
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
+            t.tv_nsec = ts.tv_nsec;
+        while (nanosleep (&t, &t) == -1) {
+            vlc_testcancel();
+            assert (errno == EINTR);
+        }
+
+        ts.tv_nsec -= 10 * 1000 * 1000;
+        if (ts.tv_nsec < 0) {
+            if (--ts.tv_sec < 0)
+                return;
+            ts.tv_nsec += 1000 * 1000 * 1000;
+        }
+    }
 
 #endif
 }
538
-- 
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
539
1.7.10.2
540