]> git.sesse.net Git - vlc/blob - include/vlc_threads_funcs.h
dc55218db367dd31e55eb97728cb3dc7247a7a9a
[vlc] / include / vlc_threads_funcs.h
1 /*****************************************************************************
2  * vlc_threads_funcs.h : threads implementation for the VideoLAN client
3  * This header provides a portable threads implementation.
4  *****************************************************************************
5  * Copyright (C) 1999-2007 the VideoLAN team
6  * $Id$
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 #if !defined( __LIBVLC__ )
29   #error You are not libvlc or one of its plugins. You cannot include this file
30 #endif
31
32 #ifndef _VLC_THREADFUNCS_H_
33 #define _VLC_THREADFUNCS_H_
34
35 #if defined( WIN32 ) && !defined ETIMEDOUT
36 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
37 #endif
38
39 /*****************************************************************************
40  * Function definitions
41  *****************************************************************************/
42 VLC_EXPORT( int,  vlc_mutex_init,    ( vlc_mutex_t * ) );
43 VLC_EXPORT( int,  vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
44 VLC_EXPORT( void,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
45 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_cond_t * ) );
46 VLC_EXPORT( void,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
47 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_threadvar_t * ) );
48 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, bool ) );
49 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
50 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
51 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
52
53 /*****************************************************************************
54  * vlc_mutex_lock: lock a mutex
55  *****************************************************************************/
56 #define vlc_mutex_lock( P_MUTEX )                                           \
57     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
58
59 #if defined(LIBVLC_USE_PTHREAD)
60 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
61
62 # define VLC_THREAD_ASSERT( action ) \
63     if (val) \
64         vlc_pthread_fatal (action, val, psz_file, i_line)
65 #else
66 # define VLC_THREAD_ASSERT (void)0
67 #endif
68
69 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
70                                     vlc_mutex_t * p_mutex )
71 {
72 #if defined( UNDER_CE )
73     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
74
75     EnterCriticalSection( &p_mutex->csection );
76
77 #elif defined( WIN32 )
78     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
79
80     WaitForSingleObject( *p_mutex, INFINITE );
81
82 #elif defined( HAVE_KERNEL_SCHEDULER_H )
83     acquire_sem( p_mutex->lock );
84
85 #elif defined(LIBVLC_USE_PTHREAD)
86 #   define vlc_assert_locked( m ) \
87            assert (pthread_mutex_lock (m) == EDEADLK)
88     int val = pthread_mutex_lock( p_mutex );
89     VLC_THREAD_ASSERT ("locking mutex");
90
91 #endif
92 }
93
94 #ifndef vlc_assert_locked
95 # define vlc_assert_locked( m ) (void)0
96 #endif
97
98 /*****************************************************************************
99  * vlc_mutex_unlock: unlock a mutex
100  *****************************************************************************/
101 #define vlc_mutex_unlock( P_MUTEX )                                         \
102     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
103
104 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
105                                       vlc_mutex_t *p_mutex )
106 {
107 #if defined( UNDER_CE )
108     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
109
110     LeaveCriticalSection( &p_mutex->csection );
111
112 #elif defined( WIN32 )
113     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
114
115     ReleaseMutex( *p_mutex );
116
117 #elif defined( HAVE_KERNEL_SCHEDULER_H )
118     release_sem( p_mutex->lock );
119
120 #elif defined(LIBVLC_USE_PTHREAD)
121     int val = pthread_mutex_unlock( p_mutex );
122     VLC_THREAD_ASSERT ("unlocking mutex");
123
124 #endif
125 }
126
127 /*****************************************************************************
128  * vlc_mutex_destroy: destroy a mutex
129  *****************************************************************************/
130 #define vlc_mutex_destroy( P_MUTEX )                                        \
131     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
132
133 /*****************************************************************************
134  * vlc_cond_init: initialize a condition
135  *****************************************************************************/
136 #define vlc_cond_init( P_THIS, P_COND )                                     \
137     __vlc_cond_init( P_COND )
138
139 /*****************************************************************************
140  * vlc_cond_signal: start a thread on condition completion
141  *****************************************************************************/
142 #define vlc_cond_signal( P_COND )                                           \
143     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
144
145 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
146                                       vlc_cond_t *p_condvar )
147 {
148 #if defined( UNDER_CE ) || defined( WIN32 )
149     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
150
151     /* Release one waiting thread if one is available. */
152     /* For this trick to work properly, the vlc_cond_signal must be surrounded
153      * by a mutex. This will prevent another thread from stealing the signal */
154     /* PulseEvent() only works if none of the waiting threads is suspended.
155      * This is particularily problematic under a debug session.
156      * as documented in http://support.microsoft.com/kb/q173260/ */
157     PulseEvent( p_condvar->event );
158
159 #elif defined( HAVE_KERNEL_SCHEDULER_H )
160     while( p_condvar->thread != -1 )
161     {
162         thread_info info;
163         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
164             return;
165
166         if( info.state != B_THREAD_SUSPENDED )
167         {
168             /* The  waiting thread is not suspended so it could
169              * have been interrupted beetwen the unlock and the
170              * suspend_thread line. That is why we sleep a little
171              * before retesting p_condver->thread. */
172             snooze( 10000 );
173         }
174         else
175         {
176             /* Ok, we have to wake up that thread */
177             resume_thread( p_condvar->thread );
178         }
179     }
180
181 #elif defined(LIBVLC_USE_PTHREAD)
182     int val = pthread_cond_signal( p_condvar );
183     VLC_THREAD_ASSERT ("signaling condition variable");
184
185 #endif
186 }
187
188 /*****************************************************************************
189  * vlc_cond_wait: wait until condition completion
190  *****************************************************************************/
191 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
192     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
193
194 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
195                                     vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
196 {
197 #if defined( UNDER_CE )
198     p_condvar->i_waiting_threads++;
199     LeaveCriticalSection( &p_mutex->csection );
200     WaitForSingleObject( p_condvar->event, INFINITE );
201     p_condvar->i_waiting_threads--;
202
203     /* Reacquire the mutex before returning. */
204     vlc_mutex_lock( p_mutex );
205
206 #elif defined( WIN32 )
207     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
208
209     /* Increase our wait count */
210     p_condvar->i_waiting_threads++;
211     SignalObjectAndWait( *p_mutex, p_condvar->event, INFINITE, FALSE );
212     p_condvar->i_waiting_threads--;
213
214     /* Reacquire the mutex before returning. */
215     vlc_mutex_lock( p_mutex );
216
217 #elif defined( HAVE_KERNEL_SCHEDULER_H )
218     /* The p_condvar->thread var is initialized before the unlock because
219      * it enables to identify when the thread is interrupted beetwen the
220      * unlock line and the suspend_thread line */
221     p_condvar->thread = find_thread( NULL );
222     vlc_mutex_unlock( p_mutex );
223     suspend_thread( p_condvar->thread );
224     p_condvar->thread = -1;
225
226     vlc_mutex_lock( p_mutex );
227
228 #elif defined(LIBVLC_USE_PTHREAD)
229     int val = pthread_cond_wait( p_condvar, p_mutex );
230     VLC_THREAD_ASSERT ("waiting on condition");
231
232 #endif
233 }
234
235
236 /*****************************************************************************
237  * vlc_cond_timedwait: wait until condition completion or expiration
238  *****************************************************************************
239  * Returns 0 if object signaled, an error code in case of timeout or error.
240  *****************************************************************************/
241 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE )                      \
242     __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE  )
243
244 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
245                                         vlc_cond_t *p_condvar,
246                                         vlc_mutex_t *p_mutex,
247                                         mtime_t deadline )
248 {
249 #if defined( UNDER_CE )
250     mtime_t delay_ms = (deadline - mdate())/1000;
251
252     DWORD result;
253     if( delay_ms < 0 )
254         delay_ms = 0;
255
256     p_condvar->i_waiting_threads++;
257     LeaveCriticalSection( &p_mutex->csection );
258     result = WaitForSingleObject( p_condvar->event, delay_ms );
259     p_condvar->i_waiting_threads--;
260
261     /* Reacquire the mutex before returning. */
262     vlc_mutex_lock( p_mutex );
263
264     if(result == WAIT_TIMEOUT)
265        return ETIMEDOUT; /* this error is perfectly normal */
266
267 #elif defined( WIN32 )
268     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
269
270     DWORD result;
271
272     mtime_t delay_ms = (deadline - mdate())/1000;
273     if( delay_ms < 0 )
274         delay_ms = 0;
275
276     /* Increase our wait count */
277     p_condvar->i_waiting_threads++;
278     result = SignalObjectAndWait( *p_mutex, p_condvar->event,
279                                   delay_ms, FALSE );
280     p_condvar->i_waiting_threads--;
281
282     /* Reacquire the mutex before returning. */
283     vlc_mutex_lock( p_mutex );
284     if(result == WAIT_TIMEOUT)
285        return ETIMEDOUT; /* this error is perfectly normal */
286
287 #elif defined( HAVE_KERNEL_SCHEDULER_H )
288 #   error Unimplemented
289
290 #elif defined(LIBVLC_USE_PTHREAD)
291     lldiv_t d = lldiv( deadline, 1000000 );
292     struct timespec ts = { d.quot, d.rem * 1000 };
293
294     int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
295     if (val == ETIMEDOUT)
296         return ETIMEDOUT; /* this error is perfectly normal */
297     VLC_THREAD_ASSERT ("timed-waiting on condition");
298
299 #endif
300
301     return 0;
302 }
303
304 /*****************************************************************************
305  * vlc_cond_destroy: destroy a condition
306  *****************************************************************************/
307 #define vlc_cond_destroy( P_COND )                                          \
308     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
309
310 /*****************************************************************************
311  * vlc_threadvar_create: create a thread-local variable
312  *****************************************************************************/
313 #define vlc_threadvar_create( PTHIS, P_TLS )                                 \
314    __vlc_threadvar_create( P_TLS )
315
316 /*****************************************************************************
317  * vlc_threadvar_set: create: set the value of a thread-local variable
318  *****************************************************************************/
319 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
320 {
321     int i_ret;
322
323 #if defined( HAVE_KERNEL_SCHEDULER_H )
324     i_ret = EINVAL;
325
326 #elif defined( UNDER_CE ) || defined( WIN32 )
327     i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0;
328
329 #elif defined(LIBVLC_USE_PTHREAD)
330     i_ret = pthread_setspecific( *p_tls, p_value );
331
332 #endif
333
334     return i_ret;
335 }
336
337 /*****************************************************************************
338  * vlc_threadvar_get: create: get the value of a thread-local variable
339  *****************************************************************************/
340 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
341 {
342     void *p_ret;
343
344 #if defined( HAVE_KERNEL_SCHEDULER_H )
345     p_ret = NULL;
346
347 #elif defined( UNDER_CE ) || defined( WIN32 )
348     p_ret = TlsGetValue( *p_tls );
349
350 #elif defined(LIBVLC_USE_PTHREAD)
351     p_ret = pthread_getspecific( *p_tls );
352
353 #endif
354
355     return p_ret;
356 }
357
358 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
359 typedef struct
360 {
361     pthread_spinlock_t  spin;
362 } vlc_spinlock_t;
363
364 /**
365  * Initializes a spinlock.
366  */
367 static inline int vlc_spin_init (vlc_spinlock_t *spin)
368 {
369     return pthread_spin_init (&spin->spin, PTHREAD_PROCESS_PRIVATE);
370 }
371
372 /**
373  * Acquires a spinlock.
374  */
375 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
376 {
377     int val = pthread_spin_lock (&spin->spin);
378     assert (val == 0);
379     (void)val;
380 }
381
382 /**
383  * Releases a spinlock.
384  */
385 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
386 {
387     int val = pthread_spin_unlock (&spin->spin);
388     assert (val == 0);
389     (void)val;
390 }
391
392 /**
393  * Deinitializes a spinlock.
394  */
395 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
396 {
397     int val = pthread_spin_destroy (&spin->spin);
398     assert (val == 0);
399     (void)val;
400 }
401
402 #elif defined( WIN32 )
403
404 typedef CRITICAL_SECTION vlc_spinlock_t;
405
406 /**
407  * Initializes a spinlock.
408  */
409 static inline int vlc_spin_init (vlc_spinlock_t *spin)
410 {
411     return !InitializeCriticalSectionAndSpinCount(spin, 4000);
412 }
413
414 /**
415  * Acquires a spinlock.
416  */
417 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
418 {
419     EnterCriticalSection(spin);
420 }
421
422 /**
423  * Releases a spinlock.
424  */
425 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
426 {
427     LeaveCriticalSection(spin);
428 }
429
430 /**
431  * Deinitializes a spinlock.
432  */
433 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
434 {
435     DeleteCriticalSection(spin);
436 }
437
438
439 #else
440
441 /* Fallback to plain mutexes if spinlocks are not available */
442 typedef vlc_mutex_t vlc_spinlock_t;
443
444 static inline int vlc_spin_init (vlc_spinlock_t *spin)
445 {
446     return vlc_mutex_init (spin);
447 }
448
449 # define vlc_spin_lock    vlc_mutex_lock
450 # define vlc_spin_unlock  vlc_mutex_unlock
451 # define vlc_spin_destroy vlc_mutex_destroy
452 #endif
453
454 /*****************************************************************************
455  * vlc_thread_create: create a thread
456  *****************************************************************************/
457 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
458     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
459
460 /*****************************************************************************
461  * vlc_thread_set_priority: set the priority of the calling thread
462  *****************************************************************************/
463 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
464     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
465
466 /*****************************************************************************
467  * vlc_thread_ready: tell the parent thread we were successfully spawned
468  *****************************************************************************/
469 #define vlc_thread_ready( P_THIS )                                          \
470     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
471
472 /*****************************************************************************
473  * vlc_thread_join: wait until a thread exits
474  *****************************************************************************/
475 #define vlc_thread_join( P_THIS )                                           \
476     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )
477
478 #endif