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