]> git.sesse.net Git - vlc/blob - include/vlc_threads.h
Partial atomic replacement for antiquated GCC versions (fixes #6825)
[vlc] / include / vlc_threads.h
1 /*****************************************************************************
2  * vlc_threads.h : threads implementation for the VideoLAN client
3  * This header provides portable declarations for mutexes & conditions
4  *****************************************************************************
5  * Copyright (C) 1999, 2002 VLC authors and VideoLAN
6  * Copyright © 2007-2008 Rémi Denis-Courmont
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@via.ecp.fr>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *          Christophe Massiot <massiot@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #ifndef VLC_THREADS_H_
29 #define VLC_THREADS_H_
30
31 /**
32  * \file
33  * This file defines structures and functions for handling threads in vlc
34  *
35  */
36
37 #if defined( UNDER_CE )
38 #elif defined( WIN32 )
39 #   include <process.h>                                         /* Win32 API */
40
41 #elif defined( __OS2__ )                                        /* OS/2 API  */
42 #   include <errno.h>
43
44 #   define pthread_sigmask  sigprocmask
45
46 #else                                         /* pthreads (like Linux & BSD) */
47 #   define LIBVLC_USE_PTHREAD 1
48 #   define LIBVLC_USE_PTHREAD_CANCEL 1
49 #   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
50
51 #   include <unistd.h> /* _POSIX_SPIN_LOCKS */
52 #   include <pthread.h>
53
54 /* Unnamed POSIX semaphores not supported on Mac OS X, use Mach semaphores instead */
55 #   if defined (__APPLE__)
56 #      include <mach/semaphore.h>
57 #      include <mach/task.h>
58 #   else
59 #      include <semaphore.h>
60 #   endif
61
62 #endif
63
64 /*****************************************************************************
65  * Constants
66  *****************************************************************************/
67
68 /* Thread priorities */
69 #ifdef __APPLE__
70 #   define VLC_THREAD_PRIORITY_LOW      0
71 #   define VLC_THREAD_PRIORITY_INPUT   22
72 #   define VLC_THREAD_PRIORITY_AUDIO   22
73 #   define VLC_THREAD_PRIORITY_VIDEO    0
74 #   define VLC_THREAD_PRIORITY_OUTPUT  22
75 #   define VLC_THREAD_PRIORITY_HIGHEST 22
76
77 #elif defined(LIBVLC_USE_PTHREAD)
78 #   define VLC_THREAD_PRIORITY_LOW      0
79 #   define VLC_THREAD_PRIORITY_INPUT   10
80 #   define VLC_THREAD_PRIORITY_AUDIO    5
81 #   define VLC_THREAD_PRIORITY_VIDEO    0
82 #   define VLC_THREAD_PRIORITY_OUTPUT  15
83 #   define VLC_THREAD_PRIORITY_HIGHEST 20
84
85 #elif defined(WIN32) || defined(UNDER_CE)
86 /* Define different priorities for WinNT/2K/XP and Win9x/Me */
87 #   define VLC_THREAD_PRIORITY_LOW 0
88 #   define VLC_THREAD_PRIORITY_INPUT \
89         THREAD_PRIORITY_ABOVE_NORMAL
90 #   define VLC_THREAD_PRIORITY_AUDIO \
91         THREAD_PRIORITY_HIGHEST
92 #   define VLC_THREAD_PRIORITY_VIDEO 0
93 #   define VLC_THREAD_PRIORITY_OUTPUT \
94         THREAD_PRIORITY_ABOVE_NORMAL
95 #   define VLC_THREAD_PRIORITY_HIGHEST \
96         THREAD_PRIORITY_TIME_CRITICAL
97
98 #elif defined(__OS2__)
99 #   define VLC_THREAD_PRIORITY_LOW      0
100 #   define VLC_THREAD_PRIORITY_INPUT    MAKESHORT( PRTYD_MAXIMUM / 2, PRTYC_REGULAR )
101 #   define VLC_THREAD_PRIORITY_AUDIO    MAKESHORT( PRTYD_MAXIMUM, PRTYC_REGULAR )
102 #   define VLC_THREAD_PRIORITY_VIDEO    0
103 #   define VLC_THREAD_PRIORITY_OUTPUT   MAKESHORT( PRTYD_MAXIMUM / 2, PRTYC_REGULAR )
104 #   define VLC_THREAD_PRIORITY_HIGHEST  MAKESHORT( 0, PRTYC_TIMECRITICAL )
105
106 #else
107 #   define VLC_THREAD_PRIORITY_LOW 0
108 #   define VLC_THREAD_PRIORITY_INPUT 0
109 #   define VLC_THREAD_PRIORITY_AUDIO 0
110 #   define VLC_THREAD_PRIORITY_VIDEO 0
111 #   define VLC_THREAD_PRIORITY_OUTPUT 0
112 #   define VLC_THREAD_PRIORITY_HIGHEST 0
113
114 #endif
115
116 /*****************************************************************************
117  * Type definitions
118  *****************************************************************************/
119
120 #if defined (LIBVLC_USE_PTHREAD)
121 typedef pthread_t       vlc_thread_t;
122 typedef pthread_mutex_t vlc_mutex_t;
123 #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
124 typedef pthread_cond_t  vlc_cond_t;
125 #define VLC_STATIC_COND  PTHREAD_COND_INITIALIZER
126 typedef pthread_rwlock_t vlc_rwlock_t;
127 #define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
128 typedef pthread_key_t   vlc_threadvar_t;
129 typedef struct vlc_timer *vlc_timer_t;
130
131 #if defined (__APPLE__)
132 typedef semaphore_t     vlc_sem_t;
133 #else
134 typedef sem_t           vlc_sem_t;
135 #endif
136
137 #elif defined( WIN32 )
138 typedef struct vlc_thread *vlc_thread_t;
139
140 typedef struct
141 {
142     bool dynamic;
143     union
144     {
145         struct
146         {
147             bool locked;
148             unsigned long contention;
149         };
150         CRITICAL_SECTION mutex;
151     };
152 } vlc_mutex_t;
153 #define VLC_STATIC_MUTEX { false, { { false, 0 } } }
154
155 typedef struct
156 {
157     HANDLE   handle;
158     unsigned clock;
159 } vlc_cond_t;
160 #define VLC_STATIC_COND { 0, 0 }
161
162 typedef HANDLE  vlc_sem_t;
163
164 typedef struct
165 {
166     vlc_mutex_t   mutex;
167     vlc_cond_t    wait;
168     unsigned long readers;
169     DWORD         writer;
170 } vlc_rwlock_t;
171 #define VLC_STATIC_RWLOCK \
172     { VLC_STATIC_MUTEX, VLC_STATIC_COND, 0, 0 }
173
174 typedef struct vlc_threadvar *vlc_threadvar_t;
175 typedef struct vlc_timer *vlc_timer_t;
176
177 #elif defined( __OS2__ )
178 typedef struct vlc_thread *vlc_thread_t;
179
180 typedef struct
181 {
182     bool dynamic;
183     union
184     {
185         struct
186         {
187             bool locked;
188             unsigned long contention;
189         };
190         HMTX hmtx;
191     };
192 } vlc_mutex_t;
193
194 #define VLC_STATIC_MUTEX { false, { { false, 0 } } }
195
196 typedef struct
197 {
198     HEV      hev;
199     unsigned clock;
200 } vlc_cond_t;
201
202 #define VLC_STATIC_COND { 0, 0 }
203
204 typedef struct
205 {
206     HEV  hev;
207     HMTX wait_mutex;
208     HMTX count_mutex;
209     int  count;
210 } vlc_sem_t;
211
212 typedef struct
213 {
214     vlc_mutex_t   mutex;
215     vlc_cond_t    wait;
216     unsigned long readers;
217     int           writer;
218 } vlc_rwlock_t;
219 #define VLC_STATIC_RWLOCK \
220     { VLC_STATIC_MUTEX, VLC_STATIC_COND, 0, 0 }
221
222 typedef struct vlc_threadvar *vlc_threadvar_t;
223 typedef struct vlc_timer *vlc_timer_t;
224
225 #endif
226
227 #if defined( WIN32 ) && !defined ETIMEDOUT
228 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
229 #endif
230
231 /*****************************************************************************
232  * Function definitions
233  *****************************************************************************/
234 VLC_API void vlc_mutex_init( vlc_mutex_t * );
235 VLC_API void vlc_mutex_init_recursive( vlc_mutex_t * );
236 VLC_API void vlc_mutex_destroy( vlc_mutex_t * );
237 VLC_API void vlc_mutex_lock( vlc_mutex_t * );
238 VLC_API int vlc_mutex_trylock( vlc_mutex_t * ) VLC_USED;
239 VLC_API void vlc_mutex_unlock( vlc_mutex_t * );
240 VLC_API void vlc_cond_init( vlc_cond_t * );
241 VLC_API void vlc_cond_init_daytime( vlc_cond_t * );
242 VLC_API void vlc_cond_destroy( vlc_cond_t * );
243 VLC_API void vlc_cond_signal(vlc_cond_t *);
244 VLC_API void vlc_cond_broadcast(vlc_cond_t *);
245 VLC_API void vlc_cond_wait(vlc_cond_t *, vlc_mutex_t *);
246 VLC_API int vlc_cond_timedwait(vlc_cond_t *, vlc_mutex_t *, mtime_t);
247 VLC_API void vlc_sem_init(vlc_sem_t *, unsigned);
248 VLC_API void vlc_sem_destroy(vlc_sem_t *);
249 VLC_API int vlc_sem_post(vlc_sem_t *);
250 VLC_API void vlc_sem_wait(vlc_sem_t *);
251
252 VLC_API void vlc_rwlock_init(vlc_rwlock_t *);
253 VLC_API void vlc_rwlock_destroy(vlc_rwlock_t *);
254 VLC_API void vlc_rwlock_rdlock(vlc_rwlock_t *);
255 VLC_API void vlc_rwlock_wrlock(vlc_rwlock_t *);
256 VLC_API void vlc_rwlock_unlock(vlc_rwlock_t *);
257 VLC_API int vlc_threadvar_create(vlc_threadvar_t * , void (*) (void *) );
258 VLC_API void vlc_threadvar_delete(vlc_threadvar_t *);
259 VLC_API int vlc_threadvar_set(vlc_threadvar_t, void *);
260 VLC_API void * vlc_threadvar_get(vlc_threadvar_t);
261
262 VLC_API int vlc_clone(vlc_thread_t *, void * (*) (void *), void *, int) VLC_USED;
263 VLC_API void vlc_cancel(vlc_thread_t);
264 VLC_API void vlc_join(vlc_thread_t, void **);
265 VLC_API void vlc_control_cancel (int cmd, ...);
266
267 VLC_API mtime_t mdate(void);
268 VLC_API void mwait(mtime_t deadline);
269 VLC_API void msleep(mtime_t delay);
270
271 #define VLC_HARD_MIN_SLEEP   10000 /* 10 milliseconds = 1 tick at 100Hz */
272 #define VLC_SOFT_MIN_SLEEP 9000000 /* 9 seconds */
273
274 #if VLC_GCC_VERSION(4,3)
275 /* Linux has 100, 250, 300 or 1000Hz
276  *
277  * HZ=100 by default on FreeBSD, but some architectures use a 1000Hz timer
278  */
279
280 static
281 __attribute__((unused))
282 __attribute__((noinline))
283 __attribute__((error("sorry, cannot sleep for such short a time")))
284 mtime_t impossible_delay( mtime_t delay )
285 {
286     (void) delay;
287     return VLC_HARD_MIN_SLEEP;
288 }
289
290 static
291 __attribute__((unused))
292 __attribute__((noinline))
293 __attribute__((warning("use proper event handling instead of short delay")))
294 mtime_t harmful_delay( mtime_t delay )
295 {
296     return delay;
297 }
298
299 # define check_delay( d ) \
300     ((__builtin_constant_p(d < VLC_HARD_MIN_SLEEP) \
301    && (d < VLC_HARD_MIN_SLEEP)) \
302        ? impossible_delay(d) \
303        : ((__builtin_constant_p(d < VLC_SOFT_MIN_SLEEP) \
304        && (d < VLC_SOFT_MIN_SLEEP)) \
305            ? harmful_delay(d) \
306            : d))
307
308 static
309 __attribute__((unused))
310 __attribute__((noinline))
311 __attribute__((error("deadlines can not be constant")))
312 mtime_t impossible_deadline( mtime_t deadline )
313 {
314     return deadline;
315 }
316
317 # define check_deadline( d ) \
318     (__builtin_constant_p(d) ? impossible_deadline(d) : d)
319 #else
320 # define check_delay(d) (d)
321 # define check_deadline(d) (d)
322 #endif
323
324 #define msleep(d) msleep(check_delay(d))
325 #define mwait(d) mwait(check_deadline(d))
326
327 VLC_API int vlc_timer_create(vlc_timer_t *, void (*) (void *), void *) VLC_USED;
328 VLC_API void vlc_timer_destroy(vlc_timer_t);
329 VLC_API void vlc_timer_schedule(vlc_timer_t, bool, mtime_t, mtime_t);
330 VLC_API unsigned vlc_timer_getoverrun(vlc_timer_t) VLC_USED;
331
332 VLC_API unsigned vlc_GetCPUCount(void);
333
334 #ifndef LIBVLC_USE_PTHREAD_CANCEL
335 enum {
336     VLC_CLEANUP_PUSH,
337     VLC_CLEANUP_POP,
338 };
339 #endif
340
341 VLC_API int vlc_savecancel(void);
342 VLC_API void vlc_restorecancel(int state);
343 VLC_API void vlc_testcancel(void);
344
345 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
346 /**
347  * Registers a new procedure to run if the thread is cancelled (or otherwise
348  * exits prematurely). Any call to vlc_cleanup_push() <b>must</b> paired with a
349  * call to either vlc_cleanup_pop() or vlc_cleanup_run(). Branching into or out
350  * of the block between these two function calls is not allowed (read: it will
351  * likely crash the whole process). If multiple procedures are registered,
352  * they are handled in last-in first-out order.
353  *
354  * @param routine procedure to call if the thread ends
355  * @param arg argument for the procedure
356  */
357 # define vlc_cleanup_push( routine, arg ) pthread_cleanup_push (routine, arg)
358
359 /**
360  * Removes a cleanup procedure that was previously registered with
361  * vlc_cleanup_push().
362  */
363 # define vlc_cleanup_pop( ) pthread_cleanup_pop (0)
364
365 /**
366  * Removes a cleanup procedure that was previously registered with
367  * vlc_cleanup_push(), and executes it.
368  */
369 # define vlc_cleanup_run( ) pthread_cleanup_pop (1)
370 #else
371 typedef struct vlc_cleanup_t vlc_cleanup_t;
372
373 struct vlc_cleanup_t
374 {
375     vlc_cleanup_t *next;
376     void         (*proc) (void *);
377     void          *data;
378 };
379
380 /* This macros opens a code block on purpose. This is needed for multiple
381  * calls within a single function. This also prevent Win32 developers from
382  * writing code that would break on POSIX (POSIX opens a block as well). */
383 # define vlc_cleanup_push( routine, arg ) \
384     do { \
385         vlc_cleanup_t vlc_cleanup_data = { NULL, routine, arg, }; \
386         vlc_control_cancel (VLC_CLEANUP_PUSH, &vlc_cleanup_data)
387
388 # define vlc_cleanup_pop( ) \
389         vlc_control_cancel (VLC_CLEANUP_POP); \
390     } while (0)
391
392 # define vlc_cleanup_run( ) \
393         vlc_control_cancel (VLC_CLEANUP_POP); \
394         vlc_cleanup_data.proc (vlc_cleanup_data.data); \
395     } while (0)
396
397 /* poll() with cancellation */
398 static inline int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout)
399 {
400     vlc_testcancel ();
401
402     while (timeout > 50)
403     {
404         int val = poll (fds, nfds, timeout);
405         if (val != 0)
406             return val;
407         timeout -= 50;
408         vlc_testcancel ();
409     }
410
411     return poll (fds, nfds, timeout);
412 }
413 # define poll(u,n,t) vlc_poll(u, n, t)
414
415 #endif /* LIBVLC_USE_PTHREAD_CANCEL */
416
417 static inline void vlc_cleanup_lock (void *lock)
418 {
419     vlc_mutex_unlock ((vlc_mutex_t *)lock);
420 }
421 #define mutex_cleanup_push( lock ) vlc_cleanup_push (vlc_cleanup_lock, lock)
422
423 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
424 typedef pthread_spinlock_t vlc_spinlock_t;
425
426 /**
427  * Initializes a spinlock.
428  */
429 static inline void vlc_spin_init (vlc_spinlock_t *spin)
430 {
431     if (pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE))
432         abort ();
433 }
434
435 /**
436  * Acquires a spinlock.
437  */
438 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
439 {
440     pthread_spin_lock (spin);
441 }
442
443 /**
444  * Releases a spinlock.
445  */
446 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
447 {
448     pthread_spin_unlock (spin);
449 }
450
451 /**
452  * Deinitializes a spinlock.
453  */
454 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
455 {
456     pthread_spin_destroy (spin);
457 }
458
459 #elif defined (WIN32) && !defined (UNDER_CE)
460
461 typedef CRITICAL_SECTION vlc_spinlock_t;
462
463 /**
464  * Initializes a spinlock.
465  */
466 static inline void vlc_spin_init (vlc_spinlock_t *spin)
467 {
468     if (!InitializeCriticalSectionAndSpinCount(spin, 4000))
469         abort ();
470 }
471
472 /**
473  * Acquires a spinlock.
474  */
475 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
476 {
477     EnterCriticalSection(spin);
478 }
479
480 /**
481  * Releases a spinlock.
482  */
483 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
484 {
485     LeaveCriticalSection(spin);
486 }
487
488 /**
489  * Deinitializes a spinlock.
490  */
491 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
492 {
493     DeleteCriticalSection(spin);
494 }
495
496 #else
497
498 /* Fallback to plain mutexes if spinlocks are not available */
499 typedef vlc_mutex_t vlc_spinlock_t;
500
501 static inline void vlc_spin_init (vlc_spinlock_t *spin)
502 {
503     vlc_mutex_init (spin);
504 }
505
506 # define vlc_spin_lock    vlc_mutex_lock
507 # define vlc_spin_unlock  vlc_mutex_unlock
508 # define vlc_spin_destroy vlc_mutex_destroy
509 #endif
510
511 /**
512  * Issues a full memory barrier.
513  */
514 #if defined (__APPLE__)
515 # include <libkern/OSAtomic.h> /* OSMemoryBarrier() */
516 #endif
517 static inline void barrier (void)
518 {
519 #if defined (__GNUC__) && !defined (__APPLE__) && \
520             ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
521     __sync_synchronize ();
522 #elif defined(__APPLE__)
523     OSMemoryBarrier ();
524 #elif defined(__powerpc__)
525     asm volatile ("sync":::"memory");
526 #elif 0 // defined(__i386__) /*  Requires SSE2 support */
527     asm volatile ("mfence":::"memory");
528 #else
529     vlc_spinlock_t spin;
530     vlc_spin_init (&spin);
531     vlc_spin_lock (&spin);
532     vlc_spin_unlock (&spin);
533     vlc_spin_destroy (&spin);
534 #endif
535 }
536
537 #ifdef __cplusplus
538 /**
539  * Helper C++ class to lock a mutex.
540  * The mutex is locked when the object is created, and unlocked when the object
541  * is destroyed.
542  */
543 class vlc_mutex_locker
544 {
545     private:
546         vlc_mutex_t *lock;
547     public:
548         vlc_mutex_locker (vlc_mutex_t *m) : lock (m)
549         {
550             vlc_mutex_lock (lock);
551         }
552
553         ~vlc_mutex_locker (void)
554         {
555             vlc_mutex_unlock (lock);
556         }
557 };
558 #endif
559
560 enum {
561    VLC_AVCODEC_MUTEX = 0,
562    VLC_GCRYPT_MUTEX,
563    VLC_XLIB_MUTEX,
564    VLC_MOSAIC_MUTEX,
565    VLC_HIGHLIGHT_MUTEX,
566    VLC_ATOMIC_MUTEX,
567    /* Insert new entry HERE */
568    VLC_MAX_MUTEX
569 };
570
571 VLC_API void vlc_global_mutex( unsigned, bool );
572 #define vlc_global_lock( n ) vlc_global_mutex( n, true )
573 #define vlc_global_unlock( n ) vlc_global_mutex( n, false )
574
575 #endif /* !_VLC_THREADS_H */