]> git.sesse.net Git - vlc/blob - include/vlc_threads_funcs.h
b2d6213c40f185eaefe6f929a8873a62a5fb5ece
[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 VideoLAN
6  * $Id: vlc_threads_funcs.h,v 1.8 2002/11/10 18:04:22 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 #   ifndef UNDER_CE
236     else if( p_condvar->i_win9x_cv == 1 )
237     {
238         /* Wait for the gate to be open */
239         WaitForSingleObject( p_condvar->event, INFINITE );
240
241         if( p_condvar->i_waiting_threads )
242         {
243             /* Using a semaphore exposes us to a race condition. It is
244              * possible for another thread to start waiting on the semaphore
245              * just after we signaled it and thus steal the signal.
246              * We have to prevent new threads from entering the cond_wait(). */
247             ResetEvent( p_condvar->event );
248
249             /* A semaphore is used here because Win9x doesn't have
250              * SignalObjectAndWait() and thus a race condition exists
251              * during the time we release the mutex and the time we start
252              * waiting on the event (more precisely, the signal can sometimes
253              * be missed by the waiting thread if we use PulseEvent()). */
254             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
255         }
256     }
257     else
258     {
259         if( p_condvar->i_waiting_threads )
260         {
261             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
262
263             /* Wait for the last thread to be awakened */
264             WaitForSingleObject( p_condvar->event, INFINITE );
265         }
266     }
267 #   endif
268     return 0;
269
270 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
271     i_result = pthread_cond_signal( &p_condvar->cond );
272     if ( i_result )
273     {
274         i_thread = (int)pthread_self();
275         psz_error = strerror(i_result);
276     }
277
278 #elif defined( HAVE_CTHREADS_H )
279     /* condition_signal() */
280     if ( p_condvar->queue.head || p_condvar->implications )
281     {
282         cond_signal( (condition_t)p_condvar );
283     }
284     return 0;
285
286 #elif defined( HAVE_KERNEL_SCHEDULER_H )
287     if( p_condvar == NULL )
288     {
289         i_result = B_BAD_VALUE;
290     }
291     else if( p_condvar->init < 2000 )
292     {
293         i_result = B_NO_INIT;
294     }
295     else
296     {
297         while( p_condvar->thread != -1 )
298         {
299             thread_info info;
300             if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
301             {
302                 return 0;
303             }
304
305             if( info.state != B_THREAD_SUSPENDED )
306             {
307                 /* The  waiting thread is not suspended so it could
308                  * have been interrupted beetwen the unlock and the
309                  * suspend_thread line. That is why we sleep a little
310                  * before retesting p_condver->thread. */
311                 snooze( 10000 );
312             }
313             else
314             {
315                 /* Ok, we have to wake up that thread */
316                 resume_thread( p_condvar->thread );
317                 return 0;
318             }
319         }
320         i_result = 0;
321     }
322 #endif
323
324     if( i_result )
325     {
326         msg_Err( p_condvar->p_this,
327                  "thread %d: cond_signal failed at %s:%d (%d:%s)",
328                  i_thread, psz_file, i_line, i_result, psz_error );
329     }
330
331     return i_result;
332 }
333
334 /*****************************************************************************
335  * vlc_cond_broadcast: start all threads waiting on condition completion
336  *****************************************************************************/
337 /*
338  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
339  * Only works with pthreads, st, win32
340  * You need to adapt it for others
341  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
342  */
343 #define vlc_cond_broadcast( P_COND )                                        \
344     __vlc_cond_broadcast( __FILE__, __LINE__, P_COND )
345
346 static inline int __vlc_cond_broadcast( char * psz_file, int i_line,
347                                         vlc_cond_t *p_condvar )
348 {
349     int i_result;
350     /* In case of error : */
351     int i_thread = -1;
352     const char * psz_error = "";
353
354 #if defined( PTH_INIT_IN_PTH_H )
355     i_result = pth_cond_notify( &p_condvar->cond, FALSE );
356
357 #elif defined( ST_INIT_IN_ST_H )
358     i_result = st_cond_broadcast( p_condvar->cond );
359
360 #elif defined( WIN32 )
361     int i;
362
363     /* Release all waiting threads. */
364     if( !p_condvar->semaphore )
365     {
366         for( i = p_condvar->i_waiting_threads; i > 0; i-- )
367         {
368             PulseEvent( p_condvar->event );
369         }
370     }
371 #   ifndef UNDER_CE
372     else if( p_condvar->i_win9x_cv == 1 )
373     {
374         /* Wait for the gate to be open */
375         WaitForSingleObject( p_condvar->event, INFINITE );
376
377         if( p_condvar->i_waiting_threads )
378         {
379             /* Using a semaphore exposes us to a race condition. It is
380              * possible for another thread to start waiting on the semaphore
381              * just after we signaled it and thus steal the signal.
382              * We have to prevent new threads from entering the cond_wait(). */
383             ResetEvent( p_condvar->event );
384
385             /* A semaphore is used here because Win9x doesn't have
386              * SignalObjectAndWait() and thus a race condition exists
387              * during the time we release the mutex and the time we start
388              * waiting on the event (more precisely, the signal can sometimes
389              * be missed by the waiting thread if we use PulseEvent()). */
390             ReleaseSemaphore( p_condvar->semaphore,
391                               p_condvar->i_waiting_threads, 0 );
392         }
393     }
394     else
395     {
396         if( p_condvar->i_waiting_threads )
397         {
398             ReleaseSemaphore( p_condvar->semaphore,
399                               p_condvar->i_waiting_threads, 0 );
400
401             /* Wait for the last thread to be awakened */
402             WaitForSingleObject( p_condvar->event, INFINITE );
403         }
404     }
405 #   endif
406     return 0;
407
408 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
409     i_result = pthread_cond_broadcast( &p_condvar->cond );
410     if ( i_result )
411     {
412         i_thread = (int)pthread_self();
413         psz_error = strerror(i_result);
414     }
415
416 #elif defined( HAVE_CTHREADS_H )
417     /* condition_signal() */
418     if ( p_condvar->queue.head || p_condvar->implications )
419     {
420         cond_signal( (condition_t)p_condvar );
421     }
422     return 0;
423
424 #elif defined( HAVE_KERNEL_SCHEDULER_H )
425     if( p_condvar == NULL )
426     {
427         i_result = B_BAD_VALUE;
428     }
429     else if( p_condvar->init < 2000 )
430     {
431         i_result = B_NO_INIT;
432     }
433     else
434     {
435         while( p_condvar->thread != -1 )
436         {
437             thread_info info;
438             if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
439             {
440                 return 0;
441             }
442
443             if( info.state != B_THREAD_SUSPENDED )
444             {
445                 /* The  waiting thread is not suspended so it could
446                  * have been interrupted beetwen the unlock and the
447                  * suspend_thread line. That is why we sleep a little
448                  * before retesting p_condver->thread. */
449                 snooze( 10000 );
450             }
451             else
452             {
453                 /* Ok, we have to wake up that thread */
454                 resume_thread( p_condvar->thread );
455                 return 0;
456             }
457         }
458         i_result = 0;
459     }
460 #endif
461
462     if( i_result )
463     {
464         msg_Err( p_condvar->p_this,
465                  "thread %d: cond_broadcast failed at %s:%d (%d:%s)",
466                  i_thread, psz_file, i_line, i_result, psz_error );
467     }
468
469     return i_result;
470 }
471
472 /*****************************************************************************
473  * vlc_cond_wait: wait until condition completion
474  *****************************************************************************/
475 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
476     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
477
478 static inline int __vlc_cond_wait( char * psz_file, int i_line,
479                                    vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
480 {
481     int i_result;
482     /* In case of error : */
483     int i_thread = -1;
484     const char * psz_error = "";
485
486 #if defined( PTH_INIT_IN_PTH_H )
487     i_result = pth_cond_await( &p_condvar->cond, &p_mutex->mutex, NULL );
488
489 #elif defined( ST_INIT_IN_ST_H )
490     st_mutex_unlock( p_mutex->mutex );
491     i_result = st_cond_wait( p_condvar->cond );
492     st_mutex_lock( p_mutex->mutex );
493
494 #elif defined( WIN32 )
495     if( !p_condvar->semaphore )
496     {
497         /* Increase our wait count */
498         p_condvar->i_waiting_threads++;
499
500         if( p_mutex->mutex )
501         {
502             /* It is only possible to atomically release the mutex and
503              * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
504              * SignalObjectAndWait(). */
505             p_condvar->SignalObjectAndWait( p_mutex->mutex,
506                                             p_condvar->event,
507                                             INFINITE, FALSE );
508         }
509         else
510         {
511             LeaveCriticalSection( &p_mutex->csection );
512             WaitForSingleObject( p_condvar->event, INFINITE );
513         }
514
515         p_condvar->i_waiting_threads--;
516     }
517     else if( p_condvar->i_win9x_cv == 1 )
518     {
519         int i_waiting_threads;
520
521         /* Wait for the gate to be open */
522         WaitForSingleObject( p_condvar->event, INFINITE );
523
524         /* Increase our wait count */
525         p_condvar->i_waiting_threads++;
526
527         LeaveCriticalSection( &p_mutex->csection );
528         WaitForSingleObject( p_condvar->semaphore, INFINITE );
529
530         /* Decrement and test must be atomic */
531         EnterCriticalSection( &p_condvar->csection );
532
533         /* Decrease our wait count */
534         i_waiting_threads = --p_condvar->i_waiting_threads;
535
536         LeaveCriticalSection( &p_condvar->csection );
537
538         /* Reopen the gate if we were the last waiting thread */
539         if( !i_waiting_threads )
540             SetEvent( p_condvar->event );
541     }
542     else
543     {
544         int i_waiting_threads;
545
546         /* Increase our wait count */
547         p_condvar->i_waiting_threads++;
548
549         LeaveCriticalSection( &p_mutex->csection );
550         WaitForSingleObject( p_condvar->semaphore, INFINITE );
551
552         /* Decrement and test must be atomic */
553         EnterCriticalSection( &p_condvar->csection );
554
555         /* Decrease our wait count */
556         i_waiting_threads = --p_condvar->i_waiting_threads;
557
558         LeaveCriticalSection( &p_condvar->csection );
559
560         /* Signal that the last waiting thread just went through */
561         if( !i_waiting_threads )
562             SetEvent( p_condvar->event );
563     }
564
565     /* Reacquire the mutex before returning. */
566     vlc_mutex_lock( p_mutex );
567
568     return 0;
569
570 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
571
572 #   ifdef DEBUG
573     /* In debug mode, timeout */
574     struct timeval now;
575     struct timespec timeout;
576
577     for( ; ; )
578     {
579         gettimeofday( &now, NULL );
580         timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
581         timeout.tv_nsec = now.tv_usec * 1000;
582
583         i_result = pthread_cond_timedwait( &p_condvar->cond, &p_mutex->mutex,
584                                            &timeout );
585
586         if( i_result == ETIMEDOUT )
587         {
588             msg_Warn( p_condvar->p_this,
589                       "thread %d: possible deadlock detected "
590                       "in cond_wait at %s:%d (%s)", pthread_self(),
591                       psz_file, i_line, strerror(i_result) );
592         }
593         else break;
594     }
595 #   else
596     i_result = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
597 #   endif
598
599     if ( i_result )
600     {
601         i_thread = (int)pthread_self();
602         psz_error = strerror(i_result);
603     }
604
605 #elif defined( HAVE_CTHREADS_H )
606     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
607     return 0;
608
609 #elif defined( HAVE_KERNEL_SCHEDULER_H )
610     if( p_condvar == NULL )
611     {
612         i_result = B_BAD_VALUE;
613     }
614     else if( p_mutex == NULL )
615     {
616         i_result = B_BAD_VALUE;
617     }
618     else if( p_condvar->init < 2000 )
619     {
620         i_result = B_NO_INIT;
621     }
622
623     /* The p_condvar->thread var is initialized before the unlock because
624      * it enables to identify when the thread is interrupted beetwen the
625      * unlock line and the suspend_thread line */
626     p_condvar->thread = find_thread( NULL );
627     vlc_mutex_unlock( p_mutex );
628     suspend_thread( p_condvar->thread );
629     p_condvar->thread = -1;
630
631     vlc_mutex_lock( p_mutex );
632     return 0;
633
634 #endif
635
636     if( i_result )
637     {
638         msg_Err( p_condvar->p_this,
639                  "thread %d: cond_wait failed at %s:%d (%d:%s)",
640                  i_thread, psz_file, i_line, i_result, psz_error );
641     }
642
643     return i_result;
644 }
645
646 /*****************************************************************************
647  * vlc_cond_destroy: destroy a condition
648  *****************************************************************************/
649 #define vlc_cond_destroy( P_COND )                                          \
650     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
651
652 /*****************************************************************************
653  * vlc_thread_create: create a thread
654  *****************************************************************************/
655 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
656     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
657
658 /*****************************************************************************
659  * vlc_thread_ready: tell the parent thread we were successfully spawned
660  *****************************************************************************/
661 #define vlc_thread_ready( P_THIS )                                          \
662     __vlc_thread_ready( VLC_OBJECT(P_THIS) )
663
664 /*****************************************************************************
665  * vlc_thread_join: wait until a thread exits
666  *****************************************************************************/
667 #define vlc_thread_join( P_THIS )                                           \
668     __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ )