]> git.sesse.net Git - vlc/blob - include/threads.h
* ./BUGS: added a list of known bugs. Please add your findings!
[vlc] / include / threads.h
1 /*****************************************************************************
2  * threads.h : threads implementation for the VideoLAN client
3  * This header provides a portable threads implementation.
4  *****************************************************************************
5  * Copyright (C) 1999, 2000 VideoLAN
6  * $Id: threads.h,v 1.34 2002/01/04 14:01:34 sam Exp $
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 #include <stdio.h>
27
28 #if defined(GPROF) || defined(DEBUG)
29 #   include <sys/time.h>
30 #endif
31
32 #if defined( PTH_INIT_IN_PTH_H )                                  /* GNU Pth */
33 #   include <pth.h>
34
35 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )  /* pthreads (like Linux & BSD) */
36 #   include <pthread.h>
37 /* This is not prototyped under Linux, though it exists. */
38 int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
39
40 #elif defined( HAVE_CTHREADS_H )                                  /* GNUMach */
41 #   include <cthreads.h>
42
43 #elif defined( HAVE_KERNEL_SCHEDULER_H )                             /* BeOS */
44 #   undef MAX
45 #   undef MIN
46 #   include <kernel/OS.h>
47 #   include <kernel/scheduler.h>
48 #   include <byteorder.h>
49
50 #elif defined( WIN32 )
51 #define WIN32_LEAN_AND_MEAN
52 #   include <windows.h>
53 #   include <process.h>
54
55 #else
56 #   error no threads available on your system !
57
58 #endif
59
60 /*****************************************************************************
61  * Constants
62  *****************************************************************************
63  * These constants are used by all threads in *_CreateThread() and
64  * *_DestroyThreads() functions. Since those calls are non-blocking, an integer
65  * value is used as a shared flag to represent the status of the thread.
66  *****************************************************************************/
67
68 /* Void status - this value can be used to make sure no operation is currently
69  * in progress on the concerned thread in an array of recorded threads */
70 #define THREAD_NOP          0                            /* nothing happened */
71
72 /* Creation status */
73 #define THREAD_CREATE       10                     /* thread is initializing */
74 #define THREAD_START        11                          /* thread has forked */
75 #define THREAD_READY        19                            /* thread is ready */
76
77 /* Destructions status */
78 #define THREAD_DESTROY      20            /* destruction order has been sent */
79 #define THREAD_END          21        /* destruction order has been received */
80 #define THREAD_OVER         29             /* thread does not exist any more */
81
82 /* Error status */
83 #define THREAD_ERROR        30                           /* an error occured */
84 #define THREAD_FATAL        31  /* an fatal error occured - program must end */
85
86 /*****************************************************************************
87  * Types definition
88  *****************************************************************************/
89
90 #if defined( PTH_INIT_IN_PTH_H )
91 typedef pth_t            vlc_thread_t;
92 typedef pth_mutex_t      vlc_mutex_t;
93 typedef pth_cond_t       vlc_cond_t;
94
95 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
96 typedef pthread_t        vlc_thread_t;
97 typedef pthread_mutex_t  vlc_mutex_t;
98 typedef pthread_cond_t   vlc_cond_t;
99
100 #elif defined( HAVE_CTHREADS_H )
101 typedef cthread_t        vlc_thread_t;
102
103 /* Those structs are the ones defined in /include/cthreads.h but we need
104  * to handle (*foo) where foo is a (mutex_t) while they handle (foo) where
105  * foo is a (mutex_t*) */
106 typedef struct s_mutex {
107     spin_lock_t held;
108     spin_lock_t lock;
109     char *name;
110     struct cthread_queue queue;
111 } vlc_mutex_t;
112
113 typedef struct s_condition {
114     spin_lock_t lock;
115     struct cthread_queue queue;
116     char *name;
117     struct cond_imp *implications;
118 } vlc_cond_t;
119
120 #elif defined( HAVE_KERNEL_SCHEDULER_H )
121 /* This is the BeOS implementation of the vlc threads, note that the mutex is
122  * not a real mutex and the cond_var is not like a pthread cond_var but it is
123  * enough for what wee need */
124
125 typedef thread_id vlc_thread_t;
126
127 typedef struct
128 {
129     int32           init;
130     sem_id          lock;
131 } vlc_mutex_t;
132
133 typedef struct
134 {
135     int32           init;
136     thread_id       thread;
137 } vlc_cond_t;
138
139 #elif defined( WIN32 )
140 typedef HANDLE           vlc_thread_t;
141 typedef CRITICAL_SECTION vlc_mutex_t;
142
143 typedef struct
144 {
145     int             i_waiting_threads;
146     HANDLE          signal;
147 } vlc_cond_t;
148
149 typedef unsigned (__stdcall *PTHREAD_START) (void *);
150
151 #endif
152
153 typedef void *(*vlc_thread_func_t)(void *p_data);
154
155 /*****************************************************************************
156  * Prototypes
157  *****************************************************************************/
158
159 #ifdef GPROF
160 /* Wrapper function for profiling */
161 static void *      vlc_thread_wrapper ( void *p_wrapper );
162
163 #   ifdef WIN32
164
165 #       define ITIMER_REAL 1
166 #       define ITIMER_PROF 2
167
168 struct itimerval
169 {
170     struct timeval it_value;
171     struct timeval it_interval;
172 };
173
174 int setitimer(int kind, const struct itimerval* itnew, struct itimerval* itold);
175
176 #   endif /* WIN32 */
177
178 typedef struct wrapper_s
179 {
180     /* Data lock access */
181     vlc_mutex_t lock;
182     vlc_cond_t  wait;
183
184     /* Data used to spawn the real thread */
185     vlc_thread_func_t func;
186     void *p_data;
187
188     /* Profiling timer passed to the thread */
189     struct itimerval itimer;
190
191 } wrapper_t;
192
193 #endif /* GPROF */
194
195 /*****************************************************************************
196  * vlc_threads_init: initialize threads system
197  *****************************************************************************/
198 static __inline__ int vlc_threads_init( void )
199 {
200 #if defined( PTH_INIT_IN_PTH_H )
201     return pth_init();
202
203 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
204     return 0;
205
206 #elif defined( HAVE_CTHREADS_H )
207     return 0;
208
209 #elif defined( HAVE_KERNEL_SCHEDULER_H )
210     return 0;
211
212 #elif defined( WIN32 )
213     return 0;
214
215 #endif
216 }
217
218 /*****************************************************************************
219  * vlc_threads_end: stop threads system
220  *****************************************************************************/
221 static __inline__ int vlc_threads_end( void )
222 {
223 #if defined( PTH_INIT_IN_PTH_H )
224     return pth_kill();
225
226 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
227     return 0;
228
229 #elif defined( HAVE_CTHREADS_H )
230     return 0;
231
232 #elif defined( HAVE_KERNEL_SCHEDULER_H )
233     return 0;
234
235 #elif defined( WIN32 )
236     return 0;
237
238 #endif
239 }
240
241 /*****************************************************************************
242  * vlc_mutex_init: initialize a mutex
243  *****************************************************************************/
244 static __inline__ int vlc_mutex_init( vlc_mutex_t *p_mutex )
245 {
246 #if defined( PTH_INIT_IN_PTH_H )
247     return pth_mutex_init( p_mutex );
248
249 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
250 #   if defined(DEBUG) && defined(SYS_LINUX)
251     /* Create error-checking mutex to detect threads problems more easily. */
252     pthread_mutexattr_t attr;
253     int                 i_result;
254
255     pthread_mutexattr_init( &attr );
256     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
257     i_result = pthread_mutex_init( p_mutex, &attr );
258     pthread_mutexattr_destroy( &attr );
259     return( i_result );
260 #   endif
261
262     return pthread_mutex_init( p_mutex, NULL );
263
264 #elif defined( HAVE_CTHREADS_H )
265     mutex_init( p_mutex );
266     return 0;
267
268 #elif defined( HAVE_KERNEL_SCHEDULER_H )
269
270     /* check the arguments and whether it's already been initialized */
271     if( p_mutex == NULL )
272     {
273         return B_BAD_VALUE;
274     }
275
276     if( p_mutex->init == 9999 )
277     {
278         return EALREADY;
279     }
280
281     p_mutex->lock = create_sem( 1, "BeMutex" );
282     if( p_mutex->lock < B_NO_ERROR )
283     {
284         return( -1 );
285     }
286
287     p_mutex->init = 9999;
288     return B_OK;
289
290 #elif defined( WIN32 )
291     InitializeCriticalSection( p_mutex );
292     return 0;
293
294 #endif
295 }
296
297 /*****************************************************************************
298  * vlc_mutex_lock: lock a mutex
299  *****************************************************************************/
300 #ifdef DEBUG
301 #   define vlc_mutex_lock( P_MUTEX )                                        \
302         _vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
303 #else
304 #   define vlc_mutex_lock( P_MUTEX )                                        \
305         _vlc_mutex_lock( NULL, 0, P_MUTEX )
306 #endif
307
308 static __inline__ int _vlc_mutex_lock( char * psz_file, int i_line,
309                                        vlc_mutex_t *p_mutex )
310 {
311 #if defined( PTH_INIT_IN_PTH_H )
312     return pth_mutex_acquire( p_mutex, TRUE, NULL );
313
314 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
315     int i_return = pthread_mutex_lock( p_mutex );
316     if( i_return )
317     {
318         intf_ErrMsg( "thread %d error: mutex_lock failed at %s:%d (%s)",
319                      pthread_self(), psz_file, i_line, strerror(i_return) );
320     }
321     return i_return;
322
323 #elif defined( HAVE_CTHREADS_H )
324     mutex_lock( p_mutex );
325     return 0;
326
327 #elif defined( HAVE_KERNEL_SCHEDULER_H )
328     status_t err;
329
330     if( !p_mutex )
331     {
332         return B_BAD_VALUE;
333     }
334
335     if( p_mutex->init < 2000 )
336     {
337         return B_NO_INIT;
338     }
339
340     err = acquire_sem( p_mutex->lock );
341     return err;
342
343 #elif defined( WIN32 )
344     EnterCriticalSection( p_mutex );
345     return 0;
346
347 #endif
348 }
349
350 /*****************************************************************************
351  * vlc_mutex_unlock: unlock a mutex
352  *****************************************************************************/
353 #ifdef DEBUG
354 #   define vlc_mutex_unlock( P_MUTEX )                                      \
355         _vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
356 #else
357 #   define vlc_mutex_unlock( P_MUTEX )                                      \
358         _vlc_mutex_unlock( NULL, 0, P_MUTEX )
359 #endif
360
361 static __inline__ int _vlc_mutex_unlock( char * psz_file, int i_line,
362                                          vlc_mutex_t *p_mutex )
363 {
364 #if defined( PTH_INIT_IN_PTH_H )
365     return pth_mutex_release( p_mutex );
366
367 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
368     int i_return = pthread_mutex_unlock( p_mutex );
369     if( i_return )
370     {
371         intf_ErrMsg( "thread %d error: mutex_unlock failed at %s:%d (%s)",
372                      pthread_self(), psz_file, i_line, strerror(i_return) );
373     }
374     return i_return;
375
376 #elif defined( HAVE_CTHREADS_H )
377     mutex_unlock( p_mutex );
378     return 0;
379
380 #elif defined( HAVE_KERNEL_SCHEDULER_H )
381     if( !p_mutex)
382     {
383         return B_BAD_VALUE;
384     }
385
386     if( p_mutex->init < 2000 )
387     {
388         return B_NO_INIT;
389     }
390
391     release_sem( p_mutex->lock );
392     return B_OK;
393
394 #elif defined( WIN32 )
395     LeaveCriticalSection( p_mutex );
396     return 0;
397
398 #endif
399 }
400
401 /*****************************************************************************
402  * vlc_mutex_destroy: destroy a mutex
403  *****************************************************************************/
404 #ifdef DEBUG
405 #   define vlc_mutex_destroy( P_MUTEX )                                     \
406         _vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
407 #else
408 #   define vlc_mutex_destroy( P_MUTEX )                                     \
409         _vlc_mutex_destroy( NULL, 0, P_MUTEX )
410 #endif
411
412 static __inline__ int _vlc_mutex_destroy( char * psz_file, int i_line,
413                                           vlc_mutex_t *p_mutex )
414 {
415 #if defined( PTH_INIT_IN_PTH_H )
416     return 0;
417
418 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )    
419     int i_return = pthread_mutex_destroy( p_mutex );
420     if( i_return )
421     {
422         intf_ErrMsg( "thread %d error: mutex_destroy failed at %s:%d (%s)",
423                      pthread_self(), psz_file, i_line, strerror(i_return) );
424     }
425     return i_return;
426
427 #elif defined( HAVE_CTHREADS_H )
428     return 0;
429
430 #elif defined( HAVE_KERNEL_SCHEDULER_H )
431     if( p_mutex->init == 9999 )
432     {
433         delete_sem( p_mutex->lock );
434     }
435
436     p_mutex->init = 0;
437     return B_OK;
438
439 #elif defined( WIN32 )
440     DeleteCriticalSection( p_mutex );
441     return 0;
442
443 #endif    
444 }
445
446 /*****************************************************************************
447  * vlc_cond_init: initialize a condition
448  *****************************************************************************/
449 static __inline__ int vlc_cond_init( vlc_cond_t *p_condvar )
450 {
451 #if defined( PTH_INIT_IN_PTH_H )
452     return pth_cond_init( p_condvar );
453
454 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
455     return pthread_cond_init( p_condvar, NULL );
456
457 #elif defined( HAVE_CTHREADS_H )
458     /* condition_init() */
459     spin_lock_init( &p_condvar->lock );
460     cthread_queue_init( &p_condvar->queue );
461     p_condvar->name = 0;
462     p_condvar->implications = 0;
463
464     return 0;
465
466 #elif defined( HAVE_KERNEL_SCHEDULER_H )
467     if( !p_condvar )
468     {
469         return B_BAD_VALUE;
470     }
471
472     if( p_condvar->init == 9999 )
473     {
474         return EALREADY;
475     }
476
477     p_condvar->thread = -1;
478     p_condvar->init = 9999;
479     return 0;
480
481 #elif defined( WIN32 )
482     /* initialise counter */
483     p_condvar->i_waiting_threads = 0;
484
485     /* Create an auto-reset event. */
486     p_condvar->signal = CreateEvent( NULL, /* no security */
487                                      FALSE,  /* auto-reset event */
488                                      FALSE,  /* non-signaled initially */
489                                      NULL ); /* unnamed */
490
491     return( !p_condvar->signal );
492     
493 #endif
494 }
495
496 /*****************************************************************************
497  * vlc_cond_signal: start a thread on condition completion
498  *****************************************************************************/
499 static __inline__ int vlc_cond_signal( vlc_cond_t *p_condvar )
500 {
501 #if defined( PTH_INIT_IN_PTH_H )
502     return pth_cond_notify( p_condvar, FALSE );
503
504 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
505     return pthread_cond_signal( p_condvar );
506
507 #elif defined( HAVE_CTHREADS_H )
508     /* condition_signal() */
509     if ( p_condvar->queue.head || p_condvar->implications )
510     {
511         cond_signal( (condition_t)p_condvar );
512     }
513     return 0;
514
515 #elif defined( HAVE_KERNEL_SCHEDULER_H )
516     if( !p_condvar )
517     {
518         return B_BAD_VALUE;
519     }
520
521     if( p_condvar->init < 2000 )
522     {
523         return B_NO_INIT;
524     }
525
526     while( p_condvar->thread != -1 )
527     {
528         thread_info info;
529         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
530         {
531             return 0;
532         }
533
534         if( info.state != B_THREAD_SUSPENDED )
535         {
536             /* The  waiting thread is not suspended so it could
537              * have been interrupted beetwen the unlock and the
538              * suspend_thread line. That is why we sleep a little
539              * before retesting p_condver->thread. */
540             snooze( 10000 );
541         }
542         else
543         {
544             /* Ok, we have to wake up that thread */
545             resume_thread( p_condvar->thread );
546             return 0;
547         }
548     }
549     return 0;
550
551 #elif defined( WIN32 )
552     /* Release one waiting thread if one is available. */
553     /* For this trick to work properly, the vlc_cond_signal must be surrounded
554      * by a mutex. This will prevent another thread from stealing the signal */
555     int i_waiting_threads = p_condvar->i_waiting_threads;
556     while( p_condvar->i_waiting_threads
557            && p_condvar->i_waiting_threads == i_waiting_threads )
558     {
559         PulseEvent( p_condvar->signal );
560         Sleep( 0 ); /* deschedule the current thread */
561     }
562     return 0;
563
564 #endif
565 }
566
567 /*****************************************************************************
568  * vlc_cond_broadcast: start all threads waiting on condition completion
569  *****************************************************************************/
570 /*
571  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
572  * Only works with pthreads, you need to adapt it for others
573  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
574  */
575 static __inline__ int vlc_cond_broadcast( vlc_cond_t *p_condvar )
576 {
577 #if defined( PTH_INIT_IN_PTH_H )
578     return pth_cond_notify( p_condvar, FALSE );
579
580 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
581     return pthread_cond_broadcast( p_condvar );
582
583 #elif defined( HAVE_CTHREADS_H )
584     /* condition_signal() */
585     if ( p_condvar->queue.head || p_condvar->implications )
586     {
587         cond_signal( (condition_t)p_condvar );
588     }
589     return 0;
590
591 #elif defined( HAVE_KERNEL_SCHEDULER_H )
592     if( !p_condvar )
593     {
594         return B_BAD_VALUE;
595     }
596
597     if( p_condvar->init < 2000 )
598     {
599         return B_NO_INIT;
600     }
601
602     while( p_condvar->thread != -1 )
603     {
604         thread_info info;
605         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
606         {
607             return 0;
608         }
609
610         if( info.state != B_THREAD_SUSPENDED )
611         {
612             /* The  waiting thread is not suspended so it could
613              * have been interrupted beetwen the unlock and the
614              * suspend_thread line. That is why we sleep a little
615              * before retesting p_condver->thread. */
616             snooze( 10000 );
617         }
618         else
619         {
620             /* Ok, we have to wake up that thread */
621             resume_thread( p_condvar->thread );
622             return 0;
623         }
624     }
625     return 0;
626
627 #elif defined( WIN32 )
628     /* Release all waiting threads. */
629     /* For this trick to work properly, the vlc_cond_signal must be surrounded
630      * by a mutex. This will prevent another thread from stealing the signal */
631     while( p_condvar->i_waiting_threads )
632     {
633         PulseEvent( p_condvar->signal );
634         Sleep( 0 ); /* deschedule the current thread */
635     }
636     return 0;
637
638 #endif
639 }
640
641 /*****************************************************************************
642  * vlc_cond_wait: wait until condition completion
643  *****************************************************************************/
644 #ifdef DEBUG
645 #   define vlc_cond_wait( P_COND, P_MUTEX )                                 \
646         _vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
647 #else
648 #   define vlc_cond_wait( P_COND, P_MUTEX )                                 \
649         _vlc_cond_wait( NULL, 0, P_COND, P_MUTEX )
650 #endif
651
652 static __inline__ int _vlc_cond_wait( char * psz_file, int i_line,
653                                       vlc_cond_t *p_condvar,
654                                       vlc_mutex_t *p_mutex )
655 {
656 #if defined( PTH_INIT_IN_PTH_H )
657     return pth_cond_await( p_condvar, p_mutex, NULL );
658
659 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
660
661 #ifndef DEBUG
662     return pthread_cond_wait( p_condvar, p_mutex );
663 #else
664     /* In debug mode, timeout */
665     struct timeval now;
666     struct timespec timeout;
667     int    i_result;
668
669     for( ; ; )
670     {
671         gettimeofday( &now, NULL );
672         timeout.tv_sec = now.tv_sec + THREAD_COND_TIMEOUT;
673         timeout.tv_nsec = now.tv_usec * 1000;
674
675         if( (i_result = pthread_cond_timedwait( p_condvar, p_mutex, &timeout )) )
676         {
677             intf_WarnMsg( 1, "thread %d warning: Possible deadlock detected in cond_wait at %s:%d (%s)",
678                           pthread_self(), psz_file, i_line, strerror(i_result) );
679         }
680         else
681         {
682             return i_result;
683         }
684     }
685 #endif
686
687 #elif defined( HAVE_CTHREADS_H )
688     condition_wait( (condition_t)p_condvar, (mutex_t)p_mutex );
689     return 0;
690
691 #elif defined( HAVE_KERNEL_SCHEDULER_H )
692     if( !p_condvar )
693     {
694         return B_BAD_VALUE;
695     }
696
697     if( !p_mutex )
698     {
699         return B_BAD_VALUE;
700     }
701
702     if( p_condvar->init < 2000 )
703     {
704         return B_NO_INIT;
705     }
706
707     /* The p_condvar->thread var is initialized before the unlock because
708      * it enables to identify when the thread is interrupted beetwen the
709      * unlock line and the suspend_thread line */
710     p_condvar->thread = find_thread( NULL );
711     vlc_mutex_unlock( p_mutex );
712     suspend_thread( p_condvar->thread );
713     p_condvar->thread = -1;
714
715     vlc_mutex_lock( p_mutex );
716     return 0;
717
718 #elif defined( WIN32 )
719     /* The ideal would be to use a function which atomically releases the
720      * mutex and initiate the waiting.
721      * Unfortunately only the SignalObjectAndWait function does this and it's
722      * only supported on WinNT/2K, furthermore it cannot take multiple
723      * events as parameters.
724      *
725      * The solution we use should however fulfill all our needs (even though
726      * it is not a correct pthreads implementation)
727      */
728     int i_result;
729
730     p_condvar->i_waiting_threads ++;
731
732     /* Release the mutex */
733     vlc_mutex_unlock( p_mutex );
734
735     i_result = WaitForSingleObject( p_condvar->signal, INFINITE); 
736
737     /* maybe we should protect this with a mutex ? */
738     p_condvar->i_waiting_threads --;
739
740     /* Reacquire the mutex before returning. */
741     vlc_mutex_lock( p_mutex );
742
743     return( i_result == WAIT_FAILED );
744
745 #endif
746 }
747
748 /*****************************************************************************
749  * vlc_cond_destroy: destroy a condition
750  *****************************************************************************/
751 #ifdef DEBUG
752 #   define vlc_cond_destroy( P_COND )                                       \
753         _vlc_cond_destroy( __FILE__, __LINE__, P_COND )
754 #else
755 #   define vlc_cond_destroy( P_COND )                                       \
756         _vlc_cond_destroy( NULL, 0, P_COND )
757 #endif
758
759 static __inline__ int _vlc_cond_destroy( char * psz_file, int i_line,
760                                          vlc_cond_t *p_condvar )
761 {
762 #if defined( PTH_INIT_IN_PTH_H )
763     return 0;
764
765 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
766     int i_result = pthread_cond_destroy( p_condvar );
767     if( i_result )
768     {
769         intf_ErrMsg( "thread %d error: cond_destroy failed at %s:%d (%s)",
770                      pthread_self(), psz_file, i_line, strerror(i_result) );
771     }
772     return i_result;
773
774 #elif defined( HAVE_CTHREADS_H )
775     return 0;
776
777 #elif defined( HAVE_KERNEL_SCHEDULER_H )
778     p_condvar->init = 0;
779     return 0;
780
781 #elif defined( WIN32 )
782     return( !CloseHandle( p_condvar->signal ) );
783
784 #endif    
785 }
786
787 /*****************************************************************************
788  * vlc_thread_create: create a thread
789  *****************************************************************************/
790 #ifdef DEBUG
791 #   define vlc_thread_create( P_THREAD, PSZ_NAME, FUNC, P_DATA )            \
792         _vlc_thread_create( __FILE__, __LINE__, P_THREAD, PSZ_NAME, FUNC, P_DATA )
793 #else
794 #   define vlc_thread_create( P_THREAD, PSZ_NAME, FUNC, P_DATA )            \
795         _vlc_thread_create( NULL, 0, P_THREAD, PSZ_NAME, FUNC, P_DATA )
796 #endif
797
798 static __inline__ int _vlc_thread_create( char * psz_file, int i_line,
799                                           vlc_thread_t *p_thread,
800                                           char *psz_name,
801                                           vlc_thread_func_t func,
802                                           void *p_data )
803 {
804     int i_ret;
805
806 #ifdef GPROF
807     wrapper_t wrapper;
808
809     /* Initialize the wrapper structure */
810     wrapper.func = func;
811     wrapper.p_data = p_data;
812     getitimer( ITIMER_PROF, &wrapper.itimer );
813     vlc_mutex_init( &wrapper.lock );
814     vlc_cond_init( &wrapper.wait );
815     vlc_mutex_lock( &wrapper.lock );
816
817     /* Alter user-passed data so that we call the wrapper instead
818      * of the real function */
819     p_data = &wrapper;
820     func = vlc_thread_wrapper;
821 #endif
822
823 #if defined( PTH_INIT_IN_PTH_H )
824     *p_thread = pth_spawn( PTH_ATTR_DEFAULT, func, p_data );
825     i_ret = ( p_thread == NULL );
826
827 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
828     i_ret = pthread_create( p_thread, NULL, func, p_data );
829
830 #elif defined( HAVE_CTHREADS_H )
831     *p_thread = cthread_fork( (cthread_fn_t)func, (any_t)p_data );
832     i_ret = 0;
833
834 #elif defined( HAVE_KERNEL_SCHEDULER_H )
835     *p_thread = spawn_thread( (thread_func)func, psz_name,
836                               B_NORMAL_PRIORITY, p_data );
837     i_ret = resume_thread( *p_thread );
838
839 #elif defined( WIN32 )
840 #if 0
841     DWORD threadID;
842     /* This method is not recommended when using the MSVCRT C library,
843      * so we'll have to use _beginthreadex instead */
844     *p_thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) func, 
845                              p_data, 0, &threadID);
846 #endif
847     unsigned threadID;
848     /* When using the MSVCRT C library you have to use the _beginthreadex
849      * function instead of CreateThread, otherwise you'll end up with memory
850      * leaks and the signal function not working */
851     *p_thread = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START) func, 
852                              p_data, 0, &threadID);
853     
854     i_ret = ( *p_thread ? 0 : 1 );
855
856 #endif
857
858 #ifdef GPROF
859     if( i_ret == 0 )
860     {
861         vlc_cond_wait( &wrapper.wait, &wrapper.lock );
862     }
863
864     vlc_mutex_unlock( &wrapper.lock );
865     vlc_mutex_destroy( &wrapper.lock );
866     vlc_cond_destroy( &wrapper.wait );
867 #endif
868
869     if( i_ret == 0 )
870     {
871         intf_WarnMsg( 2, "thread info: %d (%s) has been created (%s:%d)",
872                       *p_thread, psz_name, psz_file, i_line );
873     }
874     else
875     {
876         intf_ErrMsg( "thread error: %s couldn't be created at %s:%d (%s)",
877                      psz_name, psz_file, i_line, strerror(i_ret) );
878     }
879
880     return i_ret;
881 }
882
883 /*****************************************************************************
884  * vlc_thread_exit: terminate a thread
885  *****************************************************************************/
886 static __inline__ void vlc_thread_exit( void )
887 {
888 #if defined( PTH_INIT_IN_PTH_H )
889     pth_exit( 0 );
890
891 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
892     pthread_exit( 0 );
893
894 #elif defined( HAVE_CTHREADS_H )
895     int result;
896     cthread_exit( &result );
897
898 #elif defined( HAVE_KERNEL_SCHEDULER_H )
899     exit_thread( 0 );
900
901 #elif defined( WIN32 )
902 #if 0
903     ExitThread( 0 );
904 #endif
905     /* For now we don't close the thread handles (because of race conditions).
906      * Need to be looked at. */
907     _endthreadex(0);
908
909 #endif
910 }
911
912 /*****************************************************************************
913  * vlc_thread_join: wait until a thread exits
914  *****************************************************************************/
915 #ifdef DEBUG
916 #   define vlc_thread_join( THREAD )                                        \
917         _vlc_thread_join( __FILE__, __LINE__, THREAD ) 
918 #else
919 #   define vlc_thread_join( THREAD )                                        \
920         _vlc_thread_join( NULL, 0, THREAD ) 
921 #endif
922
923 static __inline__ void _vlc_thread_join( char * psz_file, int i_line,
924                                          vlc_thread_t thread )
925 {
926     int i_ret = 0;
927
928 #if defined( PTH_INIT_IN_PTH_H )
929     i_ret = pth_join( thread, NULL );
930
931 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
932     i_ret = pthread_join( thread, NULL );
933
934 #elif defined( HAVE_CTHREADS_H )
935     cthread_join( thread );
936     i_ret = 1;
937
938 #elif defined( HAVE_KERNEL_SCHEDULER_H )
939     int32 exit_value;
940     wait_for_thread( thread, &exit_value );
941
942 #elif defined( WIN32 )
943     WaitForSingleObject( thread, INFINITE );
944
945 #endif
946
947     if( i_ret )
948     {
949         intf_ErrMsg( "thread error: thread_join(%d) failed at %s:%d (%s)",
950                      thread, psz_file, i_line, strerror(i_ret) );
951     }
952     else
953     {
954         intf_WarnMsg( 2, "thread info: %d has been joined (%s:%d)",
955                       thread, psz_file, i_line );
956     }
957 }
958
959 #ifdef GPROF
960 static void *vlc_thread_wrapper( void *p_wrapper )
961 {
962     /* Put user data in thread-local variables */
963     void *            p_data = ((wrapper_t*)p_wrapper)->p_data;
964     vlc_thread_func_t func   = ((wrapper_t*)p_wrapper)->func;
965
966     /* Set the profile timer value */
967     setitimer( ITIMER_PROF, &((wrapper_t*)p_wrapper)->itimer, NULL );
968
969     /* Tell the calling thread that we don't need its data anymore */
970     vlc_mutex_lock( &((wrapper_t*)p_wrapper)->lock );
971     vlc_cond_signal( &((wrapper_t*)p_wrapper)->wait );
972     vlc_mutex_unlock( &((wrapper_t*)p_wrapper)->lock );
973
974     /* Call the real function */
975     return func( p_data );
976 }
977 #endif
978