]> git.sesse.net Git - vlc/blob - include/vlc_threads_funcs.h
A bit of vlc/libvlc cleanup:
[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, 2002 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 /*****************************************************************************
33  * Function definitions
34  *****************************************************************************/
35 VLC_EXPORT( int,  __vlc_threads_init,  ( vlc_object_t * ) );
36 VLC_EXPORT( int,  __vlc_threads_end,   ( vlc_object_t * ) );
37 VLC_EXPORT( int,  __vlc_mutex_init,    ( vlc_object_t *, vlc_mutex_t * ) );
38 VLC_EXPORT( int,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
39 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_object_t *, vlc_cond_t * ) );
40 VLC_EXPORT( int,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
41 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, vlc_bool_t ) );
42 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
43 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
44 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
45
46
47 /*****************************************************************************
48  * vlc_threads_init: initialize threads system
49  *****************************************************************************/
50 #define vlc_threads_init( P_THIS )                                          \
51     __vlc_threads_init( VLC_OBJECT(P_THIS) )
52
53 /*****************************************************************************
54  * vlc_threads_end: deinitialize threads system
55  *****************************************************************************/
56 #define vlc_threads_end( P_THIS )                                           \
57     __vlc_threads_end( VLC_OBJECT(P_THIS) )
58
59 /*****************************************************************************
60  * vlc_mutex_init: initialize a mutex
61  *****************************************************************************/
62 #define vlc_mutex_init( P_THIS, P_MUTEX )                                   \
63     __vlc_mutex_init( VLC_OBJECT(P_THIS), P_MUTEX )
64
65 /*****************************************************************************
66  * vlc_mutex_lock: lock a mutex
67  *****************************************************************************/
68 #define vlc_mutex_lock( P_MUTEX )                                           \
69     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
70
71 #ifdef __APPLE__
72 #   define CAST_PTHREAD_TO_INT(t) (unsigned long int)(uintptr_t)(void *)t
73 #else
74 #   define CAST_PTHREAD_TO_INT(t) (unsigned long int)t
75 #endif
76
77 static inline int __vlc_mutex_lock( const char * psz_file, int i_line,
78                                     vlc_mutex_t * p_mutex )
79 {
80     int i_result;
81     /* In case of error : */
82     unsigned long int i_thread = 0;
83     const char * psz_error = "";
84
85 #if defined( PTH_INIT_IN_PTH_H )
86     i_result = ( pth_mutex_acquire( &p_mutex->mutex, FALSE, NULL ) == FALSE );
87
88 #elif defined( ST_INIT_IN_ST_H )
89     i_result = st_mutex_lock( p_mutex->mutex );
90
91 #elif defined( UNDER_CE )
92     EnterCriticalSection( &p_mutex->csection );
93     i_result = 0;
94
95 #elif defined( WIN32 )
96     if( p_mutex->mutex )
97     {
98         WaitForSingleObject( p_mutex->mutex, INFINITE );
99     }
100     else
101     {
102         EnterCriticalSection( &p_mutex->csection );
103     }
104     i_result = 0;
105
106 #elif defined( HAVE_KERNEL_SCHEDULER_H )
107     if( p_mutex == NULL )
108     {
109         i_result = B_BAD_VALUE;
110     }
111     else if( p_mutex->init < 2000 )
112     {
113         i_result = B_NO_INIT;
114     }
115     else
116     {
117         i_result = acquire_sem( p_mutex->lock );
118     }
119
120 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
121     i_result = pthread_mutex_lock( &p_mutex->mutex );
122     if ( i_result )
123     {
124         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
125         psz_error = strerror(i_result);
126     }
127
128 #elif defined( HAVE_CTHREADS_H )
129     mutex_lock( p_mutex->mutex );
130     i_result = 0;
131
132 #endif
133
134     if( i_result )
135     {
136         msg_Err( p_mutex->p_this,
137                  "thread %li: mutex_lock failed at %s:%d (%d:%s)",
138                  i_thread, psz_file, i_line, i_result, psz_error );
139     }
140     return i_result;
141 }
142
143 /*****************************************************************************
144  * vlc_mutex_unlock: unlock a mutex
145  *****************************************************************************/
146 #define vlc_mutex_unlock( P_MUTEX )                                         \
147     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
148
149 static inline int __vlc_mutex_unlock( const char * psz_file, int i_line,
150                                       vlc_mutex_t *p_mutex )
151 {
152     int i_result;
153     /* In case of error : */
154     unsigned long int i_thread = 0;
155     const char * psz_error = "";
156
157 #if defined( PTH_INIT_IN_PTH_H )
158     i_result = ( pth_mutex_release( &p_mutex->mutex ) == FALSE );
159
160 #elif defined( ST_INIT_IN_ST_H )
161     i_result = st_mutex_unlock( p_mutex->mutex );
162
163 #elif defined( UNDER_CE )
164     LeaveCriticalSection( &p_mutex->csection );
165     i_result = 0;
166
167 #elif defined( WIN32 )
168     if( p_mutex->mutex )
169     {
170         ReleaseMutex( p_mutex->mutex );
171     }
172     else
173     {
174         LeaveCriticalSection( &p_mutex->csection );
175     }
176     i_result = 0;
177
178 #elif defined( HAVE_KERNEL_SCHEDULER_H )
179     if( p_mutex == NULL )
180     {
181         i_result = B_BAD_VALUE;
182     }
183     else if( p_mutex->init < 2000 )
184     {
185         i_result = B_NO_INIT;
186     }
187     else
188     {
189         release_sem( p_mutex->lock );
190         return B_OK;
191     }
192
193 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
194     i_result = pthread_mutex_unlock( &p_mutex->mutex );
195     if ( i_result )
196     {
197         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
198         psz_error = strerror(i_result);
199     }
200
201 #elif defined( HAVE_CTHREADS_H )
202     mutex_unlock( p_mutex );
203     i_result = 0;
204
205 #endif
206
207     if( i_result )
208     {
209         msg_Err( p_mutex->p_this,
210                  "thread %li: mutex_unlock failed at %s:%d (%d:%s)",
211                  i_thread, psz_file, i_line, i_result, psz_error );
212     }
213
214     return i_result;
215 }
216
217 /*****************************************************************************
218  * vlc_mutex_destroy: destroy a mutex
219  *****************************************************************************/
220 #define vlc_mutex_destroy( P_MUTEX )                                        \
221     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
222
223 /*****************************************************************************
224  * vlc_cond_init: initialize a condition
225  *****************************************************************************/
226 #define vlc_cond_init( P_THIS, P_COND )                                     \
227     __vlc_cond_init( VLC_OBJECT(P_THIS), P_COND )
228
229 /*****************************************************************************
230  * vlc_cond_signal: start a thread on condition completion
231  *****************************************************************************/
232 #define vlc_cond_signal( P_COND )                                           \
233     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
234
235 static inline int __vlc_cond_signal( const char * psz_file, int i_line,
236                                      vlc_cond_t *p_condvar )
237 {
238     int i_result;
239     /* In case of error : */
240     unsigned long int i_thread = 0;
241     const char * psz_error = "";
242
243 #if defined( PTH_INIT_IN_PTH_H )
244     i_result = ( pth_cond_notify( &p_condvar->cond, FALSE ) == FALSE );
245
246 #elif defined( ST_INIT_IN_ST_H )
247     i_result = st_cond_signal( p_condvar->cond );
248
249 #elif defined( UNDER_CE )
250     PulseEvent( p_condvar->event );
251     i_result = 0;
252
253 #elif defined( WIN32 )
254     /* Release one waiting thread if one is available. */
255     /* For this trick to work properly, the vlc_cond_signal must be surrounded
256      * by a mutex. This will prevent another thread from stealing the signal */
257     if( !p_condvar->semaphore )
258     {
259         PulseEvent( p_condvar->event );
260     }
261     else if( p_condvar->i_win9x_cv == 1 )
262     {
263         /* Wait for the gate to be open */
264         WaitForSingleObject( p_condvar->event, INFINITE );
265
266         if( p_condvar->i_waiting_threads )
267         {
268             /* Using a semaphore exposes us to a race condition. It is
269              * possible for another thread to start waiting on the semaphore
270              * just after we signaled it and thus steal the signal.
271              * We have to prevent new threads from entering the cond_wait(). */
272             ResetEvent( p_condvar->event );
273
274             /* A semaphore is used here because Win9x doesn't have
275              * SignalObjectAndWait() and thus a race condition exists
276              * during the time we release the mutex and the time we start
277              * waiting on the event (more precisely, the signal can sometimes
278              * be missed by the waiting thread if we use PulseEvent()). */
279             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
280         }
281     }
282     else
283     {
284         if( p_condvar->i_waiting_threads )
285         {
286             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
287
288             /* Wait for the last thread to be awakened */
289             WaitForSingleObject( p_condvar->event, INFINITE );
290         }
291     }
292     i_result = 0;
293
294 #elif defined( HAVE_KERNEL_SCHEDULER_H )
295     if( p_condvar == NULL )
296     {
297         i_result = B_BAD_VALUE;
298     }
299     else if( p_condvar->init < 2000 )
300     {
301         i_result = B_NO_INIT;
302     }
303     else
304     {
305         while( p_condvar->thread != -1 )
306         {
307             thread_info info;
308             if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
309             {
310                 return 0;
311             }
312
313             if( info.state != B_THREAD_SUSPENDED )
314             {
315                 /* The  waiting thread is not suspended so it could
316                  * have been interrupted beetwen the unlock and the
317                  * suspend_thread line. That is why we sleep a little
318                  * before retesting p_condver->thread. */
319                 snooze( 10000 );
320             }
321             else
322             {
323                 /* Ok, we have to wake up that thread */
324                 resume_thread( p_condvar->thread );
325                 return 0;
326             }
327         }
328         i_result = 0;
329     }
330
331 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
332     i_result = pthread_cond_signal( &p_condvar->cond );
333     if ( i_result )
334     {
335         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
336         psz_error = strerror(i_result);
337     }
338
339 #elif defined( HAVE_CTHREADS_H )
340     /* condition_signal() */
341     if ( p_condvar->queue.head || p_condvar->implications )
342     {
343         cond_signal( (condition_t)p_condvar );
344     }
345     i_result = 0;
346
347 #endif
348
349     if( i_result )
350     {
351         msg_Err( p_condvar->p_this,
352                  "thread %li: cond_signal failed at %s:%d (%d:%s)",
353                  i_thread, psz_file, i_line, i_result, psz_error );
354     }
355
356     return i_result;
357 }
358
359 /*****************************************************************************
360  * vlc_cond_wait: wait until condition completion
361  *****************************************************************************/
362 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
363     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
364
365 static inline int __vlc_cond_wait( const char * psz_file, int i_line,
366                                    vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
367 {
368     int i_result;
369     /* In case of error : */
370     unsigned long int i_thread = 0;
371     const char * psz_error = "";
372
373 #if defined( PTH_INIT_IN_PTH_H )
374     i_result = ( pth_cond_await( &p_condvar->cond, &p_mutex->mutex, NULL )
375                  == FALSE );
376
377 #elif defined( ST_INIT_IN_ST_H )
378     st_mutex_unlock( p_mutex->mutex );
379     i_result = st_cond_wait( p_condvar->cond );
380     st_mutex_lock( p_mutex->mutex );
381
382 #elif defined( UNDER_CE )
383     p_condvar->i_waiting_threads++;
384     LeaveCriticalSection( &p_mutex->csection );
385     WaitForSingleObject( p_condvar->event, INFINITE );
386     p_condvar->i_waiting_threads--;
387
388     /* Reacquire the mutex before returning. */
389     vlc_mutex_lock( p_mutex );
390
391     i_result = 0;
392
393 #elif defined( WIN32 )
394     if( !p_condvar->semaphore )
395     {
396         /* Increase our wait count */
397         p_condvar->i_waiting_threads++;
398
399         if( p_mutex->mutex )
400         {
401             /* It is only possible to atomically release the mutex and
402              * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
403              * SignalObjectAndWait(). */
404             p_condvar->SignalObjectAndWait( p_mutex->mutex,
405                                             p_condvar->event,
406                                             INFINITE, FALSE );
407         }
408         else
409         {
410             LeaveCriticalSection( &p_mutex->csection );
411             WaitForSingleObject( p_condvar->event, INFINITE );
412         }
413
414         p_condvar->i_waiting_threads--;
415     }
416     else if( p_condvar->i_win9x_cv == 1 )
417     {
418         int i_waiting_threads;
419
420         /* Wait for the gate to be open */
421         WaitForSingleObject( p_condvar->event, INFINITE );
422
423         /* Increase our wait count */
424         p_condvar->i_waiting_threads++;
425
426         LeaveCriticalSection( &p_mutex->csection );
427         WaitForSingleObject( p_condvar->semaphore, INFINITE );
428
429         /* Decrement and test must be atomic */
430         EnterCriticalSection( &p_condvar->csection );
431
432         /* Decrease our wait count */
433         i_waiting_threads = --p_condvar->i_waiting_threads;
434
435         LeaveCriticalSection( &p_condvar->csection );
436
437         /* Reopen the gate if we were the last waiting thread */
438         if( !i_waiting_threads )
439             SetEvent( p_condvar->event );
440     }
441     else
442     {
443         int i_waiting_threads;
444
445         /* Increase our wait count */
446         p_condvar->i_waiting_threads++;
447
448         LeaveCriticalSection( &p_mutex->csection );
449         WaitForSingleObject( p_condvar->semaphore, INFINITE );
450
451         /* Decrement and test must be atomic */
452         EnterCriticalSection( &p_condvar->csection );
453
454         /* Decrease our wait count */
455         i_waiting_threads = --p_condvar->i_waiting_threads;
456
457         LeaveCriticalSection( &p_condvar->csection );
458
459         /* Signal that the last waiting thread just went through */
460         if( !i_waiting_threads )
461             SetEvent( p_condvar->event );
462     }
463
464     /* Reacquire the mutex before returning. */
465     vlc_mutex_lock( p_mutex );
466
467     i_result = 0;
468
469 #elif defined( HAVE_KERNEL_SCHEDULER_H )
470     if( p_condvar == NULL )
471     {
472         i_result = B_BAD_VALUE;
473     }
474     else if( p_mutex == NULL )
475     {
476         i_result = B_BAD_VALUE;
477     }
478     else if( p_condvar->init < 2000 )
479     {
480         i_result = B_NO_INIT;
481     }
482
483     /* The p_condvar->thread var is initialized before the unlock because
484      * it enables to identify when the thread is interrupted beetwen the
485      * unlock line and the suspend_thread line */
486     p_condvar->thread = find_thread( NULL );
487     vlc_mutex_unlock( p_mutex );
488     suspend_thread( p_condvar->thread );
489     p_condvar->thread = -1;
490
491     vlc_mutex_lock( p_mutex );
492     i_result = 0;
493
494 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
495
496 #   ifdef DEBUG
497     /* In debug mode, timeout */
498     struct timeval now;
499     struct timespec timeout;
500
501     gettimeofday( &now, NULL );
502     timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
503     timeout.tv_nsec = now.tv_usec * 1000;
504
505     i_result = pthread_cond_timedwait( &p_condvar->cond, &p_mutex->mutex,
506                                        &timeout );
507
508     if( i_result == ETIMEDOUT )
509     {
510         msg_Dbg( p_condvar->p_this,
511                   "thread %li: probable condition deadlock "
512                   "at %s:%d (%s)", CAST_PTHREAD_TO_INT(pthread_self()),
513                   psz_file, i_line, strerror(i_result) );
514
515         i_result = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
516     }
517
518 #   else
519     i_result = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
520 #   endif
521
522     if ( i_result )
523     {
524         i_thread = CAST_PTHREAD_TO_INT(pthread_self());
525         psz_error = strerror(i_result);
526     }
527
528 #elif defined( HAVE_CTHREADS_H )
529     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
530     i_result = 0;
531
532 #endif
533
534     if( i_result )
535     {
536         msg_Err( p_condvar->p_this,
537                  "thread %li: cond_wait failed at %s:%d (%d:%s)",
538                  i_thread, psz_file, i_line, i_result, psz_error );
539     }
540
541     return i_result;
542 }
543
544 /*****************************************************************************
545  * vlc_cond_destroy: destroy a condition
546  *****************************************************************************/
547 #define vlc_cond_destroy( P_COND )                                          \
548     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
549
550 /*****************************************************************************
551  * vlc_thread_create: create a thread
552  *****************************************************************************/
553 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
554     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
555
556 /*****************************************************************************
557  * vlc_thread_set_priority: set the priority of the calling thread
558  *****************************************************************************/
559 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
560     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
561
562 /*****************************************************************************
563  * vlc_thread_ready: tell the parent thread we were successfully spawned
564  *****************************************************************************/
565 #define vlc_thread_ready( P_THIS )                                          \
566     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
567
568 /*****************************************************************************
569  * vlc_thread_join: wait until a thread exits
570  *****************************************************************************/
571 #define vlc_thread_join( P_THIS )                                           \
572     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )