]> git.sesse.net Git - vlc/blob - include/vlc_threads.h
Win32: emulate static condition variables
[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 the VideoLAN team
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
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 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 General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, 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 #else                                         /* pthreads (like Linux & BSD) */
42 #   define LIBVLC_USE_PTHREAD 1
43 #   define LIBVLC_USE_PTHREAD_CANCEL 1
44 #   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
45
46 #   include <unistd.h> /* _POSIX_SPIN_LOCKS */
47 #   include <pthread.h>
48
49 /* Unnamed POSIX semaphores not supported on Mac OS X, use Mach semaphores instead */
50 #   if defined (__APPLE__)
51 #      include <mach/semaphore.h>
52 #      include <mach/task.h>
53 #   else
54 #      include <semaphore.h>
55 #   endif
56
57 #endif
58
59 /*****************************************************************************
60  * Constants
61  *****************************************************************************/
62
63 /* Thread priorities */
64 #ifdef __APPLE__
65 #   define VLC_THREAD_PRIORITY_LOW      0
66 #   define VLC_THREAD_PRIORITY_INPUT   22
67 #   define VLC_THREAD_PRIORITY_AUDIO   22
68 #   define VLC_THREAD_PRIORITY_VIDEO    0
69 #   define VLC_THREAD_PRIORITY_OUTPUT  22
70 #   define VLC_THREAD_PRIORITY_HIGHEST 22
71
72 #elif defined(LIBVLC_USE_PTHREAD)
73 #   define VLC_THREAD_PRIORITY_LOW      0
74 #   define VLC_THREAD_PRIORITY_INPUT   10
75 #   define VLC_THREAD_PRIORITY_AUDIO    5
76 #   define VLC_THREAD_PRIORITY_VIDEO    0
77 #   define VLC_THREAD_PRIORITY_OUTPUT  15
78 #   define VLC_THREAD_PRIORITY_HIGHEST 20
79
80 #elif defined(WIN32) || defined(UNDER_CE)
81 /* Define different priorities for WinNT/2K/XP and Win9x/Me */
82 #   define VLC_THREAD_PRIORITY_LOW 0
83 #   define VLC_THREAD_PRIORITY_INPUT \
84         THREAD_PRIORITY_ABOVE_NORMAL
85 #   define VLC_THREAD_PRIORITY_AUDIO \
86         THREAD_PRIORITY_HIGHEST
87 #   define VLC_THREAD_PRIORITY_VIDEO 0
88 #   define VLC_THREAD_PRIORITY_OUTPUT \
89         THREAD_PRIORITY_ABOVE_NORMAL
90 #   define VLC_THREAD_PRIORITY_HIGHEST \
91         THREAD_PRIORITY_TIME_CRITICAL
92
93 #else
94 #   define VLC_THREAD_PRIORITY_LOW 0
95 #   define VLC_THREAD_PRIORITY_INPUT 0
96 #   define VLC_THREAD_PRIORITY_AUDIO 0
97 #   define VLC_THREAD_PRIORITY_VIDEO 0
98 #   define VLC_THREAD_PRIORITY_OUTPUT 0
99 #   define VLC_THREAD_PRIORITY_HIGHEST 0
100
101 #endif
102
103 /*****************************************************************************
104  * Type definitions
105  *****************************************************************************/
106
107 #if defined (LIBVLC_USE_PTHREAD)
108 typedef pthread_t       vlc_thread_t;
109 typedef pthread_mutex_t vlc_mutex_t;
110 #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
111 typedef pthread_cond_t  vlc_cond_t;
112 #define VLC_STATIC_COND  PTHREAD_COND_INITIALIZER
113 typedef pthread_rwlock_t vlc_rwlock_t;
114 #define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
115 typedef pthread_key_t   vlc_threadvar_t;
116 typedef struct vlc_timer *vlc_timer_t;
117
118 #if defined (__APPLE__)
119 typedef semaphore_t     vlc_sem_t;
120 #else
121 typedef sem_t           vlc_sem_t;
122 #endif
123
124 #elif defined( WIN32 )
125 typedef struct vlc_thread *vlc_thread_t;
126
127 typedef struct
128 {
129     bool dynamic;
130     union
131     {
132         struct
133         {
134             bool locked;
135             unsigned long contention;
136         };
137         CRITICAL_SECTION mutex;
138     };
139 } vlc_mutex_t;
140 #define VLC_STATIC_MUTEX { false, { { false, 0 } } }
141
142 typedef struct
143 {
144     HANDLE   handle;
145     unsigned clock;
146 } vlc_cond_t;
147 #define VLC_STATIC_COND { 0, 0, 0 }
148
149 typedef HANDLE  vlc_sem_t;
150
151 typedef struct
152 {
153     vlc_mutex_t   mutex;
154     vlc_cond_t    read_wait;
155     vlc_cond_t    write_wait;
156     unsigned long readers;
157     unsigned long writers;
158     DWORD         writer;
159 } vlc_rwlock_t;
160 #define VLC_STATIC_RWLOCK \
161     { VLC_STATIC_MUTEX, VLC_STATIC_COND, VLC_STATIC_COND, 0, 0, 0 }
162
163 typedef struct vlc_threadvar *vlc_threadvar_t;
164 typedef struct vlc_timer *vlc_timer_t;
165 #endif
166
167 #if defined( WIN32 ) && !defined ETIMEDOUT
168 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
169 #endif
170
171 /*****************************************************************************
172  * Function definitions
173  *****************************************************************************/
174 VLC_API void vlc_mutex_init( vlc_mutex_t * );
175 VLC_API void vlc_mutex_init_recursive( vlc_mutex_t * );
176 VLC_API void vlc_mutex_destroy( vlc_mutex_t * );
177 VLC_API void vlc_mutex_lock( vlc_mutex_t * );
178 VLC_API int vlc_mutex_trylock( vlc_mutex_t * ) VLC_USED;
179 VLC_API void vlc_mutex_unlock( vlc_mutex_t * );
180 VLC_API void vlc_cond_init( vlc_cond_t * );
181 VLC_API void vlc_cond_init_daytime( vlc_cond_t * );
182 VLC_API void vlc_cond_destroy( vlc_cond_t * );
183 VLC_API void vlc_cond_signal(vlc_cond_t *);
184 VLC_API void vlc_cond_broadcast(vlc_cond_t *);
185 VLC_API void vlc_cond_wait(vlc_cond_t *, vlc_mutex_t *);
186 VLC_API int vlc_cond_timedwait(vlc_cond_t *, vlc_mutex_t *, mtime_t);
187 VLC_API void vlc_sem_init(vlc_sem_t *, unsigned);
188 VLC_API void vlc_sem_destroy(vlc_sem_t *);
189 VLC_API int vlc_sem_post(vlc_sem_t *);
190 VLC_API void vlc_sem_wait(vlc_sem_t *);
191
192 VLC_API void vlc_rwlock_init(vlc_rwlock_t *);
193 VLC_API void vlc_rwlock_destroy(vlc_rwlock_t *);
194 VLC_API void vlc_rwlock_rdlock(vlc_rwlock_t *);
195 VLC_API void vlc_rwlock_wrlock(vlc_rwlock_t *);
196 VLC_API void vlc_rwlock_unlock(vlc_rwlock_t *);
197 VLC_API int vlc_threadvar_create(vlc_threadvar_t * , void (*) (void *) );
198 VLC_API void vlc_threadvar_delete(vlc_threadvar_t *);
199 VLC_API int vlc_threadvar_set(vlc_threadvar_t, void *);
200 VLC_API void * vlc_threadvar_get(vlc_threadvar_t);
201
202 VLC_API int vlc_clone(vlc_thread_t *, void * (*) (void *), void *, int) VLC_USED;
203 VLC_API void vlc_cancel(vlc_thread_t);
204 VLC_API void vlc_join(vlc_thread_t, void **);
205 VLC_API void vlc_control_cancel (int cmd, ...);
206
207 VLC_API mtime_t mdate(void);
208 VLC_API void mwait(mtime_t deadline);
209 VLC_API void msleep(mtime_t delay);
210
211 #define VLC_HARD_MIN_SLEEP   10000 /* 10 milliseconds = 1 tick at 100Hz */
212 #define VLC_SOFT_MIN_SLEEP 9000000 /* 9 seconds */
213
214 #if VLC_GCC_VERSION(4,3)
215 /* Linux has 100, 250, 300 or 1000Hz
216  *
217  * HZ=100 by default on FreeBSD, but some architectures use a 1000Hz timer
218  */
219
220 static
221 __attribute__((unused))
222 __attribute__((noinline))
223 __attribute__((error("sorry, cannot sleep for such short a time")))
224 mtime_t impossible_delay( mtime_t delay )
225 {
226     (void) delay;
227     return VLC_HARD_MIN_SLEEP;
228 }
229
230 static
231 __attribute__((unused))
232 __attribute__((noinline))
233 __attribute__((warning("use proper event handling instead of short delay")))
234 mtime_t harmful_delay( mtime_t delay )
235 {
236     return delay;
237 }
238
239 # define check_delay( d ) \
240     ((__builtin_constant_p(d < VLC_HARD_MIN_SLEEP) \
241    && (d < VLC_HARD_MIN_SLEEP)) \
242        ? impossible_delay(d) \
243        : ((__builtin_constant_p(d < VLC_SOFT_MIN_SLEEP) \
244        && (d < VLC_SOFT_MIN_SLEEP)) \
245            ? harmful_delay(d) \
246            : d))
247
248 static
249 __attribute__((unused))
250 __attribute__((noinline))
251 __attribute__((error("deadlines can not be constant")))
252 mtime_t impossible_deadline( mtime_t deadline )
253 {
254     return deadline;
255 }
256
257 # define check_deadline( d ) \
258     (__builtin_constant_p(d) ? impossible_deadline(d) : d)
259 #else
260 # define check_delay(d) (d)
261 # define check_deadline(d) (d)
262 #endif
263
264 #define msleep(d) msleep(check_delay(d))
265 #define mwait(d) mwait(check_deadline(d))
266
267 VLC_API int vlc_timer_create(vlc_timer_t *, void (*) (void *), void *) VLC_USED;
268 VLC_API void vlc_timer_destroy(vlc_timer_t);
269 VLC_API void vlc_timer_schedule(vlc_timer_t, bool, mtime_t, mtime_t);
270 VLC_API unsigned vlc_timer_getoverrun(vlc_timer_t) VLC_USED;
271
272 VLC_API unsigned vlc_GetCPUCount(void);
273
274 #ifndef LIBVLC_USE_PTHREAD_CANCEL
275 enum {
276     VLC_CLEANUP_PUSH,
277     VLC_CLEANUP_POP,
278 };
279 #endif
280
281 VLC_API int vlc_savecancel(void);
282 VLC_API void vlc_restorecancel(int state);
283 VLC_API void vlc_testcancel(void);
284
285 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
286 /**
287  * Registers a new procedure to run if the thread is cancelled (or otherwise
288  * exits prematurely). Any call to vlc_cleanup_push() <b>must</b> paired with a
289  * call to either vlc_cleanup_pop() or vlc_cleanup_run(). Branching into or out
290  * of the block between these two function calls is not allowed (read: it will
291  * likely crash the whole process). If multiple procedures are registered,
292  * they are handled in last-in first-out order.
293  *
294  * @param routine procedure to call if the thread ends
295  * @param arg argument for the procedure
296  */
297 # define vlc_cleanup_push( routine, arg ) pthread_cleanup_push (routine, arg)
298
299 /**
300  * Removes a cleanup procedure that was previously registered with
301  * vlc_cleanup_push().
302  */
303 # define vlc_cleanup_pop( ) pthread_cleanup_pop (0)
304
305 /**
306  * Removes a cleanup procedure that was previously registered with
307  * vlc_cleanup_push(), and executes it.
308  */
309 # define vlc_cleanup_run( ) pthread_cleanup_pop (1)
310 #else
311 typedef struct vlc_cleanup_t vlc_cleanup_t;
312
313 struct vlc_cleanup_t
314 {
315     vlc_cleanup_t *next;
316     void         (*proc) (void *);
317     void          *data;
318 };
319
320 /* This macros opens a code block on purpose. This is needed for multiple
321  * calls within a single function. This also prevent Win32 developers from
322  * writing code that would break on POSIX (POSIX opens a block as well). */
323 # define vlc_cleanup_push( routine, arg ) \
324     do { \
325         vlc_cleanup_t vlc_cleanup_data = { NULL, routine, arg, }; \
326         vlc_control_cancel (VLC_CLEANUP_PUSH, &vlc_cleanup_data)
327
328 # define vlc_cleanup_pop( ) \
329         vlc_control_cancel (VLC_CLEANUP_POP); \
330     } while (0)
331
332 # define vlc_cleanup_run( ) \
333         vlc_control_cancel (VLC_CLEANUP_POP); \
334         vlc_cleanup_data.proc (vlc_cleanup_data.data); \
335     } while (0)
336
337 #endif /* LIBVLC_USE_PTHREAD_CANCEL */
338
339 static inline void vlc_cleanup_lock (void *lock)
340 {
341     vlc_mutex_unlock ((vlc_mutex_t *)lock);
342 }
343 #define mutex_cleanup_push( lock ) vlc_cleanup_push (vlc_cleanup_lock, lock)
344
345 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
346 typedef pthread_spinlock_t vlc_spinlock_t;
347
348 /**
349  * Initializes a spinlock.
350  */
351 static inline void vlc_spin_init (vlc_spinlock_t *spin)
352 {
353     if (pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE))
354         abort ();
355 }
356
357 /**
358  * Acquires a spinlock.
359  */
360 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
361 {
362     pthread_spin_lock (spin);
363 }
364
365 /**
366  * Releases a spinlock.
367  */
368 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
369 {
370     pthread_spin_unlock (spin);
371 }
372
373 /**
374  * Deinitializes a spinlock.
375  */
376 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
377 {
378     pthread_spin_destroy (spin);
379 }
380
381 #elif defined (WIN32) && !defined (UNDER_CE)
382
383 typedef CRITICAL_SECTION vlc_spinlock_t;
384
385 /**
386  * Initializes a spinlock.
387  */
388 static inline void vlc_spin_init (vlc_spinlock_t *spin)
389 {
390     if (!InitializeCriticalSectionAndSpinCount(spin, 4000))
391         abort ();
392 }
393
394 /**
395  * Acquires a spinlock.
396  */
397 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
398 {
399     EnterCriticalSection(spin);
400 }
401
402 /**
403  * Releases a spinlock.
404  */
405 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
406 {
407     LeaveCriticalSection(spin);
408 }
409
410 /**
411  * Deinitializes a spinlock.
412  */
413 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
414 {
415     DeleteCriticalSection(spin);
416 }
417
418 #else
419
420 /* Fallback to plain mutexes if spinlocks are not available */
421 typedef vlc_mutex_t vlc_spinlock_t;
422
423 static inline void vlc_spin_init (vlc_spinlock_t *spin)
424 {
425     vlc_mutex_init (spin);
426 }
427
428 # define vlc_spin_lock    vlc_mutex_lock
429 # define vlc_spin_unlock  vlc_mutex_unlock
430 # define vlc_spin_destroy vlc_mutex_destroy
431 #endif
432
433 /**
434  * Issues a full memory barrier.
435  */
436 #if defined (__APPLE__)
437 # include <libkern/OSAtomic.h> /* OSMemoryBarrier() */
438 #endif
439 static inline void barrier (void)
440 {
441 #if defined (__GNUC__) && !defined (__APPLE__) && \
442             ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
443     __sync_synchronize ();
444 #elif defined(__APPLE__)
445     OSMemoryBarrier ();
446 #elif defined(__powerpc__)
447     asm volatile ("sync":::"memory");
448 #elif 0 // defined(__i386__) /*  Requires SSE2 support */
449     asm volatile ("mfence":::"memory");
450 #else
451     vlc_spinlock_t spin;
452     vlc_spin_init (&spin);
453     vlc_spin_lock (&spin);
454     vlc_spin_unlock (&spin);
455     vlc_spin_destroy (&spin);
456 #endif
457 }
458
459 #ifdef __cplusplus
460 /**
461  * Helper C++ class to lock a mutex.
462  * The mutex is locked when the object is created, and unlocked when the object
463  * is destroyed.
464  */
465 class vlc_mutex_locker
466 {
467     private:
468         vlc_mutex_t *lock;
469     public:
470         vlc_mutex_locker (vlc_mutex_t *m) : lock (m)
471         {
472             vlc_mutex_lock (lock);
473         }
474
475         ~vlc_mutex_locker (void)
476         {
477             vlc_mutex_unlock (lock);
478         }
479 };
480 #endif
481
482 enum {
483    VLC_AVCODEC_MUTEX = 0,
484    VLC_GCRYPT_MUTEX,
485    VLC_XLIB_MUTEX,
486    VLC_MOSAIC_MUTEX,
487    VLC_HIGHLIGHT_MUTEX,
488    /* Insert new entry HERE */
489    VLC_MAX_MUTEX
490 };
491
492 VLC_API void vlc_global_mutex( unsigned, bool );
493 #define vlc_global_lock( n ) vlc_global_mutex( n, true )
494 #define vlc_global_unlock( n ) vlc_global_mutex( n, false )
495
496 #endif /* !_VLC_THREADS_H */