Thread cancellation on WinCE
[vlc.git] / 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 #   include <errno.h>                                           /* WinCE API */
39 #elif defined( WIN32 )
40 #   include <process.h>                                         /* Win32 API */
41 #   include <errno.h>
42
43 #else                                         /* pthreads (like Linux & BSD) */
44 #   define LIBVLC_USE_PTHREAD 1
45 #   define LIBVLC_USE_PTHREAD_CANCEL 1
46 #   define _APPLE_C_SOURCE    1 /* Proper pthread semantics on OSX */
47
48 #   include <stdlib.h> /* lldiv_t definition (only in C99) */
49 #   include <unistd.h> /* _POSIX_SPIN_LOCKS */
50 #   include <pthread.h>
51     /* Needed for pthread_cond_timedwait */
52 #   include <errno.h>
53 #   include <time.h>
54
55 #endif
56
57 /*****************************************************************************
58  * Constants
59  *****************************************************************************/
60
61 /* Thread priorities */
62 #ifdef __APPLE__
63 #   define VLC_THREAD_PRIORITY_LOW      0
64 #   define VLC_THREAD_PRIORITY_INPUT   22
65 #   define VLC_THREAD_PRIORITY_AUDIO   22
66 #   define VLC_THREAD_PRIORITY_VIDEO    0
67 #   define VLC_THREAD_PRIORITY_OUTPUT  22
68 #   define VLC_THREAD_PRIORITY_HIGHEST 22
69
70 #elif defined(LIBVLC_USE_PTHREAD)
71 #   define VLC_THREAD_PRIORITY_LOW      0
72 #   define VLC_THREAD_PRIORITY_INPUT   10
73 #   define VLC_THREAD_PRIORITY_AUDIO    5
74 #   define VLC_THREAD_PRIORITY_VIDEO    0
75 #   define VLC_THREAD_PRIORITY_OUTPUT  15
76 #   define VLC_THREAD_PRIORITY_HIGHEST 20
77
78 #elif defined(WIN32) || defined(UNDER_CE)
79 /* Define different priorities for WinNT/2K/XP and Win9x/Me */
80 #   define VLC_THREAD_PRIORITY_LOW 0
81 #   define VLC_THREAD_PRIORITY_INPUT \
82         THREAD_PRIORITY_ABOVE_NORMAL
83 #   define VLC_THREAD_PRIORITY_AUDIO \
84         THREAD_PRIORITY_HIGHEST
85 #   define VLC_THREAD_PRIORITY_VIDEO 0
86 #   define VLC_THREAD_PRIORITY_OUTPUT \
87         THREAD_PRIORITY_ABOVE_NORMAL
88 #   define VLC_THREAD_PRIORITY_HIGHEST \
89         THREAD_PRIORITY_TIME_CRITICAL
90
91 #else
92 #   define VLC_THREAD_PRIORITY_LOW 0
93 #   define VLC_THREAD_PRIORITY_INPUT 0
94 #   define VLC_THREAD_PRIORITY_AUDIO 0
95 #   define VLC_THREAD_PRIORITY_VIDEO 0
96 #   define VLC_THREAD_PRIORITY_OUTPUT 0
97 #   define VLC_THREAD_PRIORITY_HIGHEST 0
98
99 #endif
100
101 /*****************************************************************************
102  * Type definitions
103  *****************************************************************************/
104
105 #if defined (LIBVLC_USE_PTHREAD)
106 typedef pthread_t       vlc_thread_t;
107 typedef pthread_mutex_t vlc_mutex_t;
108 #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
109 typedef pthread_cond_t  vlc_cond_t;
110 typedef pthread_key_t   vlc_threadvar_t;
111
112 #elif defined( WIN32 )
113 typedef struct
114 {
115     HANDLE handle;
116     void  *(*entry) (void *);
117     void  *data;
118 #if defined( UNDER_CE )
119     HANDLE cancel_event;
120 #endif
121 } *vlc_thread_t;
122
123 typedef struct
124 {
125     CRITICAL_SECTION mutex;
126     LONG initialized;
127 } vlc_mutex_t;
128 #define VLC_STATIC_MUTEX { .initialized = 0, }
129
130 typedef HANDLE  vlc_cond_t;
131 typedef DWORD   vlc_threadvar_t;
132
133 #endif
134
135 #if defined( WIN32 ) && !defined ETIMEDOUT
136 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
137 #endif
138
139 /*****************************************************************************
140  * Function definitions
141  *****************************************************************************/
142 VLC_EXPORT( int,  vlc_mutex_init,    ( vlc_mutex_t * ) );
143 VLC_EXPORT( int,  vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
144 VLC_EXPORT( void, vlc_mutex_destroy, ( vlc_mutex_t * ) );
145 VLC_EXPORT( void, vlc_mutex_lock, ( vlc_mutex_t * ) );
146 VLC_EXPORT( void, vlc_mutex_unlock, ( vlc_mutex_t * ) );
147 VLC_EXPORT( int,  vlc_cond_init,     ( vlc_cond_t * ) );
148 VLC_EXPORT( void, vlc_cond_destroy,  ( vlc_cond_t * ) );
149 VLC_EXPORT( void, vlc_cond_signal, (vlc_cond_t *) );
150 VLC_EXPORT( void, vlc_cond_broadcast, (vlc_cond_t *) );
151 VLC_EXPORT( void, vlc_cond_wait, (vlc_cond_t *, vlc_mutex_t *) );
152 VLC_EXPORT( int, vlc_cond_timedwait, (vlc_cond_t *, vlc_mutex_t *, mtime_t) );
153 VLC_EXPORT( int, vlc_threadvar_create, (vlc_threadvar_t * , void (*) (void *) ) );
154 VLC_EXPORT( void, vlc_threadvar_delete, (vlc_threadvar_t *) );
155 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( vlc_object_t * ), int, bool ) );
156 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
157 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t * ) );
158
159 VLC_EXPORT( int, vlc_clone, (vlc_thread_t *, void * (*) (void *), void *, int) );
160 VLC_EXPORT( void, vlc_cancel, (vlc_thread_t) );
161 VLC_EXPORT( void, vlc_join, (vlc_thread_t, void **) );
162 VLC_EXPORT (void, vlc_control_cancel, (int cmd, ...));
163
164 #ifndef LIBVLC_USE_PTHREAD_CANCEL
165 enum {
166     VLC_SAVE_CANCEL,
167     VLC_RESTORE_CANCEL,
168     VLC_TEST_CANCEL,
169     VLC_DO_CANCEL,
170     VLC_CLEANUP_PUSH,
171     VLC_CLEANUP_POP,
172 };
173 #endif
174
175 VLC_EXPORT(void, vlc_thread_ready, (vlc_object_t *obj));
176 #define vlc_thread_ready(o) vlc_thread_ready(VLC_OBJECT(o))
177
178 /**
179  * Save the cancellation state and disable cancellation for the calling thread.
180  * This function must be called before entering a piece of code that is not
181  * cancellation-safe.
182  * @return Previous cancellation state (opaque value).
183  */
184 static inline int vlc_savecancel (void)
185 {
186     int state;
187 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
188     (void) pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
189 #else
190     vlc_control_cancel (VLC_SAVE_CANCEL, &state);
191 #endif
192     return state;
193 }
194
195 /**
196  * Restore the cancellation state for the calling thread.
197  * @param state previous state as returned by vlc_savecancel().
198  * @return Nothing, always succeeds.
199  */
200 static inline void vlc_restorecancel (int state)
201 {
202 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
203     (void) pthread_setcancelstate (state, NULL);
204 #else
205     vlc_control_cancel (VLC_RESTORE_CANCEL, state);
206 #endif
207 }
208
209 /**
210  * Issues an explicit deferred cancellation point.
211  * This has no effect if thread cancellation is disabled.
212  * This can be called when there is a rather slow non-sleeping operation.
213  */
214 static inline void vlc_testcancel (void)
215 {
216 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
217     pthread_testcancel ();
218 #else
219     vlc_control_cancel (VLC_TEST_CANCEL);
220 #endif
221 }
222
223 #if defined (LIBVLC_USE_PTHREAD_CANCEL)
224 /**
225  * Registers a new procedure to run if the thread is cancelled (or otherwise
226  * exits prematurely). Any call to vlc_cleanup_push() <b>must</b> paired with a
227  * call to either vlc_cleanup_pop() or vlc_cleanup_run(). Branching into or out
228  * of the block between these two function calls is not allowed (read: it will
229  * likely crash the whole process). If multiple procedures are registered,
230  * they are handled in last-in first-out order.
231  *
232  * @param routine procedure to call if the thread ends
233  * @param arg argument for the procedure
234  */
235 # define vlc_cleanup_push( routine, arg ) pthread_cleanup_push (routine, arg)
236
237 /**
238  * Removes a cleanup procedure that was previously registered with
239  * vlc_cleanup_push().
240  */
241 # define vlc_cleanup_pop( ) pthread_cleanup_pop (0)
242
243 /**
244  * Removes a cleanup procedure that was previously registered with
245  * vlc_cleanup_push(), and executes it.
246  */
247 # define vlc_cleanup_run( ) pthread_cleanup_pop (1)
248 #else
249 typedef struct vlc_cleanup_t vlc_cleanup_t;
250
251 struct vlc_cleanup_t
252 {
253     vlc_cleanup_t *next;
254     void         (*proc) (void *);
255     void          *data;
256 };
257
258 /* This macros opens a code block on purpose. This is needed for multiple
259  * calls within a single function. This also prevent Win32 developpers from
260  * writing code that would break on POSIX (POSIX opens a block as well). */
261 # define vlc_cleanup_push( routine, arg ) \
262     do { \
263         vlc_cleanup_t vlc_cleanup_data = { NULL, routine, arg, }; \
264         vlc_control_cancel (VLC_CLEANUP_PUSH, &vlc_cleanup_data)
265
266 # define vlc_cleanup_pop( ) \
267         vlc_control_cancel (VLC_CLEANUP_POP); \
268     } while (0)
269
270 # define vlc_cleanup_run( ) \
271         vlc_control_cancel (VLC_CLEANUP_POP); \
272         vlc_cleanup_data.proc (vlc_cleanup_data.data); \
273     } while (0)
274
275 #endif /* LIBVLC_USE_PTHREAD_CANCEL */
276
277 static inline void vlc_cleanup_lock (void *lock)
278 {
279     vlc_mutex_unlock ((vlc_mutex_t *)lock);
280 }
281 #define mutex_cleanup_push( lock ) vlc_cleanup_push (vlc_cleanup_lock, lock)
282
283 /*****************************************************************************
284  * vlc_threadvar_set: create: set the value of a thread-local variable
285  *****************************************************************************/
286 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
287 {
288     int i_ret;
289
290 #if defined(LIBVLC_USE_PTHREAD)
291     i_ret = pthread_setspecific( *p_tls, p_value );
292
293 #elif defined( UNDER_CE ) || defined( WIN32 )
294     i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0;
295
296 #endif
297
298     return i_ret;
299 }
300
301 /*****************************************************************************
302  * vlc_threadvar_get: create: get the value of a thread-local variable
303  *****************************************************************************/
304 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
305 {
306     void *p_ret;
307
308 #if defined(LIBVLC_USE_PTHREAD)
309     p_ret = pthread_getspecific( *p_tls );
310
311 #elif defined( UNDER_CE ) || defined( WIN32 )
312     p_ret = TlsGetValue( *p_tls );
313
314 #endif
315
316     return p_ret;
317 }
318
319 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
320 typedef pthread_spinlock_t vlc_spinlock_t;
321
322 /**
323  * Initializes a spinlock.
324  */
325 static inline int vlc_spin_init (vlc_spinlock_t *spin)
326 {
327     return pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE);
328 }
329
330 /**
331  * Acquires a spinlock.
332  */
333 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
334 {
335     pthread_spin_lock (spin);
336 }
337
338 /**
339  * Releases a spinlock.
340  */
341 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
342 {
343     pthread_spin_unlock (spin);
344 }
345
346 /**
347  * Deinitializes a spinlock.
348  */
349 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
350 {
351     pthread_spin_destroy (spin);
352 }
353
354 #elif defined( WIN32 )
355
356 typedef CRITICAL_SECTION vlc_spinlock_t;
357
358 /**
359  * Initializes a spinlock.
360  */
361 static inline int vlc_spin_init (vlc_spinlock_t *spin)
362 {
363 #ifdef UNDER_CE
364     InitializeCriticalSection(spin);
365     return 0;
366 #else
367     return !InitializeCriticalSectionAndSpinCount(spin, 4000);
368 #endif
369 }
370
371 /**
372  * Acquires a spinlock.
373  */
374 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
375 {
376     EnterCriticalSection(spin);
377 }
378
379 /**
380  * Releases a spinlock.
381  */
382 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
383 {
384     LeaveCriticalSection(spin);
385 }
386
387 /**
388  * Deinitializes a spinlock.
389  */
390 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
391 {
392     DeleteCriticalSection(spin);
393 }
394
395 #else
396
397 /* Fallback to plain mutexes if spinlocks are not available */
398 typedef vlc_mutex_t vlc_spinlock_t;
399
400 static inline int vlc_spin_init (vlc_spinlock_t *spin)
401 {
402     return vlc_mutex_init (spin);
403 }
404
405 # define vlc_spin_lock    vlc_mutex_lock
406 # define vlc_spin_unlock  vlc_mutex_unlock
407 # define vlc_spin_destroy vlc_mutex_destroy
408 #endif
409
410 /**
411  * Issues a full memory barrier.
412  */
413 #if defined (__APPLE__)
414 # include <libkern/OSAtomic.h> /* OSMemoryBarrier() */
415 #endif
416 static inline void barrier (void)
417 {
418 #if defined (__GNUC__) && \
419             ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
420     __sync_synchronize ();
421 #elif defined(__APPLE__)
422     OSMemoryBarrier ();
423 #elif defined(__powerpc__)
424     asm volatile ("sync":::"memory");
425 #elif 0 // defined(__i386__) /*  Requires SSE2 support */
426     asm volatile ("mfence":::"memory");
427 #else
428     vlc_spinlock_t spin;
429     vlc_spin_init (&spin);
430     vlc_spin_lock (&spin);
431     vlc_spin_unlock (&spin);
432     vlc_spin_destroy (&spin);
433 #endif
434 }
435
436 /*****************************************************************************
437  * vlc_thread_create: create a thread
438  *****************************************************************************/
439 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
440     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, FUNC, PRIORITY, WAIT )
441
442 /*****************************************************************************
443  * vlc_thread_set_priority: set the priority of the calling thread
444  *****************************************************************************/
445 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
446     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
447
448 /*****************************************************************************
449  * vlc_thread_join: wait until a thread exits
450  *****************************************************************************/
451 #define vlc_thread_join( P_THIS )                                           \
452     __vlc_thread_join( VLC_OBJECT(P_THIS) )
453
454 #ifdef __cplusplus
455 /**
456  * Helper C++ class to lock a mutex.
457  * The mutex is locked when the object is created, and unlocked when the object
458  * is destroyed.
459  */
460 class vlc_mutex_locker
461 {
462     private:
463         vlc_mutex_t *lock;
464     public:
465         vlc_mutex_locker (vlc_mutex_t *m) : lock (m)
466         {
467             vlc_mutex_lock (lock);
468         }
469
470         ~vlc_mutex_locker (void)
471         {
472             vlc_mutex_unlock (lock);
473         }
474 };
475 #endif
476
477 #endif /* !_VLC_THREADS_H */