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