]> git.sesse.net Git - vlc/blob - include/vlc_threads_funcs.h
Fix a little bug with the playlist interface (items swaped)
[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-2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
9  *          Samuel Hocevar <sam@via.ecp.fr>
10  *          Gildas Bazin <gbazin@netcourrier.com>
11  *          Christophe Massiot <massiot@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #if !defined( __LIBVLC__ )
29   #error You are not libvlc or one of its plugins. You cannot include this file
30 #endif
31
32 #ifndef _VLC_THREADFUNCS_H_
33 #define _VLC_THREADFUNCS_H_
34
35 #if defined( WIN32 ) && !defined ETIMEDOUT
36 #  define ETIMEDOUT 10060 /* This is the value in winsock.h. */
37 #endif
38
39 /*****************************************************************************
40  * Function definitions
41  *****************************************************************************/
42 VLC_EXPORT( int,  __vlc_mutex_init,    ( vlc_mutex_t * ) );
43 VLC_EXPORT( int,  __vlc_mutex_init_recursive, ( vlc_mutex_t * ) );
44 VLC_EXPORT( void,  __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) );
45 VLC_EXPORT( int,  __vlc_cond_init,     ( vlc_cond_t * ) );
46 VLC_EXPORT( void,  __vlc_cond_destroy,  ( const char *, int, vlc_cond_t * ) );
47 VLC_EXPORT( int, __vlc_threadvar_create, (vlc_threadvar_t * ) );
48 VLC_EXPORT( int,  __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( void * ), int, vlc_bool_t ) );
49 VLC_EXPORT( int,  __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) );
50 VLC_EXPORT( void, __vlc_thread_ready,  ( vlc_object_t * ) );
51 VLC_EXPORT( void, __vlc_thread_join,   ( vlc_object_t *, const char *, int ) );
52
53 /*****************************************************************************
54  * vlc_threads_init: initialize threads system
55  *****************************************************************************/
56 #define vlc_threads_init( P_THIS )                                          \
57     __vlc_threads_init( VLC_OBJECT(P_THIS) )
58
59 /*****************************************************************************
60  * vlc_threads_end: deinitialize threads system
61  *****************************************************************************/
62 #define vlc_threads_end( P_THIS )                                           \
63     __vlc_threads_end( VLC_OBJECT(P_THIS) )
64
65 /*****************************************************************************
66  * vlc_mutex_init: initialize a mutex
67  *****************************************************************************/
68 #define vlc_mutex_init( P_THIS, P_MUTEX )                                   \
69     __vlc_mutex_init( P_MUTEX )
70
71 /*****************************************************************************
72  * vlc_mutex_init: initialize a recursive mutex (Don't use it)
73  *****************************************************************************/
74 #define vlc_mutex_init_recursive( P_THIS, P_MUTEX )                         \
75     __vlc_mutex_init_recursive( P_MUTEX )
76
77 /*****************************************************************************
78  * vlc_mutex_lock: lock a mutex
79  *****************************************************************************/
80 #define vlc_mutex_lock( P_MUTEX )                                           \
81     __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX )
82
83 #if defined(LIBVLC_USE_PTHREAD)
84 VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line));
85
86 # define VLC_THREAD_ASSERT( action ) \
87     if (val) \
88         vlc_pthread_fatal (action, val, psz_file, i_line)
89 #else
90 # define VLC_THREAD_ASSERT (void)0
91 #endif
92
93 static inline void __vlc_mutex_lock( const char * psz_file, int i_line,
94                                     vlc_mutex_t * p_mutex )
95 {
96 #if defined( UNDER_CE )
97     EnterCriticalSection( &p_mutex->csection );
98
99 #elif defined( WIN32 )
100     if( p_mutex->mutex )
101         WaitForSingleObject( p_mutex->mutex, INFINITE );
102     else
103         EnterCriticalSection( &p_mutex->csection );
104
105 #elif defined( HAVE_KERNEL_SCHEDULER_H )
106     acquire_sem( p_mutex->lock );
107
108 #elif defined(LIBVLC_USE_PTHREAD)
109 #   define vlc_assert_locked( m ) \
110            assert (pthread_mutex_lock (&((m)->mutex)) == EDEADLK)
111     int val = pthread_mutex_lock( &p_mutex->mutex );
112     VLC_THREAD_ASSERT ("locking mutex");
113
114 #endif
115 }
116
117 #ifndef vlc_assert_locked
118 # define vlc_assert_locked( m ) (void)0
119 #endif
120
121 /*****************************************************************************
122  * vlc_mutex_unlock: unlock a mutex
123  *****************************************************************************/
124 #define vlc_mutex_unlock( P_MUTEX )                                         \
125     __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX )
126
127 static inline void __vlc_mutex_unlock( const char * psz_file, int i_line,
128                                       vlc_mutex_t *p_mutex )
129 {
130 #if defined( UNDER_CE )
131     LeaveCriticalSection( &p_mutex->csection );
132
133 #elif defined( WIN32 )
134     if( p_mutex->mutex )
135         ReleaseMutex( p_mutex->mutex );
136     else
137         LeaveCriticalSection( &p_mutex->csection );
138
139 #elif defined( HAVE_KERNEL_SCHEDULER_H )
140     release_sem( p_mutex->lock );
141
142 #elif defined(LIBVLC_USE_PTHREAD)
143     int val = pthread_mutex_unlock( &p_mutex->mutex );
144     VLC_THREAD_ASSERT ("unlocking mutex");
145
146 #endif
147 }
148
149 /*****************************************************************************
150  * vlc_mutex_destroy: destroy a mutex
151  *****************************************************************************/
152 #define vlc_mutex_destroy( P_MUTEX )                                        \
153     __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX )
154
155 /*****************************************************************************
156  * vlc_cond_init: initialize a condition
157  *****************************************************************************/
158 #define vlc_cond_init( P_THIS, P_COND )                                     \
159     __vlc_cond_init( P_COND )
160
161 /*****************************************************************************
162  * vlc_cond_signal: start a thread on condition completion
163  *****************************************************************************/
164 #define vlc_cond_signal( P_COND )                                           \
165     __vlc_cond_signal( __FILE__, __LINE__, P_COND )
166
167 static inline void __vlc_cond_signal( const char * psz_file, int i_line,
168                                       vlc_cond_t *p_condvar )
169 {
170 #if defined( UNDER_CE )
171     PulseEvent( p_condvar->event );
172
173 #elif defined( WIN32 )
174     /* Release one waiting thread if one is available. */
175     /* For this trick to work properly, the vlc_cond_signal must be surrounded
176      * by a mutex. This will prevent another thread from stealing the signal */
177     if( !p_condvar->semaphore )
178     {
179         /* PulseEvent() only works if none of the waiting threads is suspended.
180          * This is particularily problematic under a debug session.
181          * as documented in http://support.microsoft.com/kb/q173260/ */
182         PulseEvent( p_condvar->event );
183     }
184     else if( p_condvar->i_win9x_cv == 1 )
185     {
186         /* Wait for the gate to be open */
187         WaitForSingleObject( p_condvar->event, INFINITE );
188
189         if( p_condvar->i_waiting_threads )
190         {
191             /* Using a semaphore exposes us to a race condition. It is
192              * possible for another thread to start waiting on the semaphore
193              * just after we signaled it and thus steal the signal.
194              * We have to prevent new threads from entering the cond_wait(). */
195             ResetEvent( p_condvar->event );
196
197             /* A semaphore is used here because Win9x doesn't have
198              * SignalObjectAndWait() and thus a race condition exists
199              * during the time we release the mutex and the time we start
200              * waiting on the event (more precisely, the signal can sometimes
201              * be missed by the waiting thread if we use PulseEvent()). */
202             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
203         }
204     }
205     else
206     {
207         if( p_condvar->i_waiting_threads )
208         {
209             ReleaseSemaphore( p_condvar->semaphore, 1, 0 );
210
211             /* Wait for the last thread to be awakened */
212             WaitForSingleObject( p_condvar->event, INFINITE );
213         }
214     }
215
216 #elif defined( HAVE_KERNEL_SCHEDULER_H )
217     while( p_condvar->thread != -1 )
218     {
219         thread_info info;
220         if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE )
221             return;
222
223         if( info.state != B_THREAD_SUSPENDED )
224         {
225             /* The  waiting thread is not suspended so it could
226              * have been interrupted beetwen the unlock and the
227              * suspend_thread line. That is why we sleep a little
228              * before retesting p_condver->thread. */
229             snooze( 10000 );
230         }
231         else
232         {
233             /* Ok, we have to wake up that thread */
234             resume_thread( p_condvar->thread );
235         }
236     }
237
238 #elif defined(LIBVLC_USE_PTHREAD)
239     int val = pthread_cond_signal( &p_condvar->cond );
240     VLC_THREAD_ASSERT ("signaling condition variable");
241
242 #endif
243 }
244
245 /*****************************************************************************
246  * vlc_cond_wait: wait until condition completion
247  *****************************************************************************/
248 #define vlc_cond_wait( P_COND, P_MUTEX )                                     \
249     __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX  )
250
251 static inline void __vlc_cond_wait( const char * psz_file, int i_line,
252                                     vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex )
253 {
254 #if defined( UNDER_CE )
255     p_condvar->i_waiting_threads++;
256     LeaveCriticalSection( &p_mutex->csection );
257     WaitForSingleObject( p_condvar->event, INFINITE );
258     p_condvar->i_waiting_threads--;
259
260     /* Reacquire the mutex before returning. */
261     vlc_mutex_lock( p_mutex );
262
263 #elif defined( WIN32 )
264     if( !p_condvar->semaphore )
265     {
266         /* Increase our wait count */
267         p_condvar->i_waiting_threads++;
268
269         if( p_mutex->mutex )
270         {
271             /* It is only possible to atomically release the mutex and
272              * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
273              * SignalObjectAndWait(). */
274             p_condvar->SignalObjectAndWait( p_mutex->mutex,
275                                             p_condvar->event,
276                                             INFINITE, FALSE );
277         }
278         else
279         {
280             LeaveCriticalSection( &p_mutex->csection );
281             WaitForSingleObject( p_condvar->event, INFINITE );
282         }
283
284         p_condvar->i_waiting_threads--;
285     }
286     else if( p_condvar->i_win9x_cv == 1 )
287     {
288         int i_waiting_threads;
289
290         /* Wait for the gate to be open */
291         WaitForSingleObject( p_condvar->event, INFINITE );
292
293         /* Increase our wait count */
294         p_condvar->i_waiting_threads++;
295
296         LeaveCriticalSection( &p_mutex->csection );
297         WaitForSingleObject( p_condvar->semaphore, INFINITE );
298
299         /* Decrement and test must be atomic */
300         EnterCriticalSection( &p_condvar->csection );
301
302         /* Decrease our wait count */
303         i_waiting_threads = --p_condvar->i_waiting_threads;
304
305         LeaveCriticalSection( &p_condvar->csection );
306
307         /* Reopen the gate if we were the last waiting thread */
308         if( !i_waiting_threads )
309             SetEvent( p_condvar->event );
310     }
311     else
312     {
313         int i_waiting_threads;
314
315         /* Increase our wait count */
316         p_condvar->i_waiting_threads++;
317
318         LeaveCriticalSection( &p_mutex->csection );
319         WaitForSingleObject( p_condvar->semaphore, INFINITE );
320
321         /* Decrement and test must be atomic */
322         EnterCriticalSection( &p_condvar->csection );
323
324         /* Decrease our wait count */
325         i_waiting_threads = --p_condvar->i_waiting_threads;
326
327         LeaveCriticalSection( &p_condvar->csection );
328
329         /* Signal that the last waiting thread just went through */
330         if( !i_waiting_threads )
331             SetEvent( p_condvar->event );
332     }
333
334     /* Reacquire the mutex before returning. */
335     vlc_mutex_lock( p_mutex );
336
337 #elif defined( HAVE_KERNEL_SCHEDULER_H )
338     /* The p_condvar->thread var is initialized before the unlock because
339      * it enables to identify when the thread is interrupted beetwen the
340      * unlock line and the suspend_thread line */
341     p_condvar->thread = find_thread( NULL );
342     vlc_mutex_unlock( p_mutex );
343     suspend_thread( p_condvar->thread );
344     p_condvar->thread = -1;
345
346     vlc_mutex_lock( p_mutex );
347
348 #elif defined(LIBVLC_USE_PTHREAD)
349     int val = pthread_cond_wait( &p_condvar->cond, &p_mutex->mutex );
350     VLC_THREAD_ASSERT ("waiting on condition");
351
352 #endif
353 }
354
355
356 /*****************************************************************************
357  * vlc_cond_timedwait: wait until condition completion or expiration
358  *****************************************************************************
359  * Returns 0 if object signaled, an error code in case of timeout or error.
360  *****************************************************************************/
361 #define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE )                      \
362     __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE  )
363
364 static inline int __vlc_cond_timedwait( const char * psz_file, int i_line,
365                                         vlc_cond_t *p_condvar,
366                                         vlc_mutex_t *p_mutex,
367                                         mtime_t deadline )
368 {
369 #if defined( UNDER_CE )
370     mtime_t delay_ms = (deadline - mdate())/1000;
371
372     DWORD result;
373     if( delay_ms < 0 )
374         delay_ms = 0;
375
376     p_condvar->i_waiting_threads++;
377     LeaveCriticalSection( &p_mutex->csection );
378     result = WaitForSingleObject( p_condvar->event, delay_ms );
379     p_condvar->i_waiting_threads--;
380
381     /* Reacquire the mutex before returning. */
382     vlc_mutex_lock( p_mutex );
383
384     if(result == WAIT_TIMEOUT)
385        return ETIMEDOUT; /* this error is perfectly normal */
386
387 #elif defined( WIN32 )
388     DWORD result;
389
390     mtime_t delay_ms = (deadline - mdate())/1000;
391     if( delay_ms < 0 )
392         delay_ms = 0;
393
394     if( !p_condvar->semaphore )
395     {
396         /* Increase our wait count */
397         p_condvar->i_waiting_threads++;
398
399         if( p_mutex->mutex )
400         {
401             /* It is only possible to atomically release the mutex and
402              * initiate the waiting on WinNT/2K/XP. Win9x doesn't have
403              * SignalObjectAndWait(). */
404             result = p_condvar->SignalObjectAndWait( p_mutex->mutex,
405                                             p_condvar->event,
406                                             delay_ms, FALSE );
407         }
408         else
409         {
410             LeaveCriticalSection( &p_mutex->csection );
411             result = WaitForSingleObject( p_condvar->event, delay_ms );
412         }
413
414         p_condvar->i_waiting_threads--;
415     }
416     else if( p_condvar->i_win9x_cv == 1 )
417     {
418         int i_waiting_threads;
419
420         /* Wait for the gate to be open */
421         result = WaitForSingleObject( p_condvar->event, delay_ms );
422
423         /* Increase our wait count */
424         p_condvar->i_waiting_threads++;
425
426         LeaveCriticalSection( &p_mutex->csection );
427         if( !result )
428         {
429             /* recaculate remaining delay */
430             delay_ms = (deadline - mdate())/1000;
431             if( delay_ms < 0 )
432                 delay_ms = 0;
433
434             result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
435         }
436
437         /* Decrement and test must be atomic */
438         EnterCriticalSection( &p_condvar->csection );
439
440         /* Decrease our wait count */
441         i_waiting_threads = --p_condvar->i_waiting_threads;
442
443         LeaveCriticalSection( &p_condvar->csection );
444
445         /* Reopen the gate if we were the last waiting thread */
446         if( !i_waiting_threads )
447             SetEvent( p_condvar->event );
448     }
449     else
450     {
451         int i_waiting_threads;
452
453         /* Increase our wait count */
454         p_condvar->i_waiting_threads++;
455
456         LeaveCriticalSection( &p_mutex->csection );
457         result = WaitForSingleObject( p_condvar->semaphore, delay_ms );
458
459         /* Decrement and test must be atomic */
460         EnterCriticalSection( &p_condvar->csection );
461
462         /* Decrease our wait count */
463         i_waiting_threads = --p_condvar->i_waiting_threads;
464
465         LeaveCriticalSection( &p_condvar->csection );
466
467         /* Signal that the last waiting thread just went through */
468         if( !i_waiting_threads )
469             SetEvent( p_condvar->event );
470     }
471
472     /* Reacquire the mutex before returning. */
473     vlc_mutex_lock( p_mutex );
474     if(result == WAIT_TIMEOUT)
475        return ETIMEDOUT; /* this error is perfectly normal */
476
477 #elif defined( HAVE_KERNEL_SCHEDULER_H )
478 #   error Unimplemented
479
480 #elif defined(LIBVLC_USE_PTHREAD)
481     lldiv_t d = lldiv( deadline, 1000000 );
482     struct timespec ts = { d.quot, d.rem * 1000 };
483
484     int val = pthread_cond_timedwait (&p_condvar->cond, &p_mutex->mutex, &ts);
485     if (val == ETIMEDOUT)
486         return ETIMEDOUT; /* this error is perfectly normal */
487     VLC_THREAD_ASSERT ("timed-waiting on condition");
488
489 #endif
490
491     return 0;
492 }
493
494 /*****************************************************************************
495  * vlc_cond_destroy: destroy a condition
496  *****************************************************************************/
497 #define vlc_cond_destroy( P_COND )                                          \
498     __vlc_cond_destroy( __FILE__, __LINE__, P_COND )
499
500 /*****************************************************************************
501  * vlc_threadvar_create: create a thread-local variable
502  *****************************************************************************/
503 #define vlc_threadvar_create( PTHIS, P_TLS )                                 \
504    __vlc_threadvar_create( P_TLS )
505
506 /*****************************************************************************
507  * vlc_threadvar_set: create: set the value of a thread-local variable
508  *****************************************************************************/
509 static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value )
510 {
511     int i_ret;
512
513 #if defined( HAVE_KERNEL_SCHEDULER_H )
514     return -1;
515
516 #elif defined( UNDER_CE ) || defined( WIN32 )
517     i_ret = ( TlsSetValue( p_tls->handle, p_value ) != 0 );
518
519 #elif defined(LIBVLC_USE_PTHREAD)
520     i_ret = pthread_setspecific( p_tls->handle, p_value );
521
522 #endif
523
524     return i_ret;
525 }
526
527 /*****************************************************************************
528  * vlc_threadvar_get: create: get the value of a thread-local variable
529  *****************************************************************************/
530 static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls )
531 {
532     void* p_ret;
533
534 #if defined( HAVE_KERNEL_SCHEDULER_H )
535     p_ret = NULL;
536 #elif defined( UNDER_CE ) || defined( WIN32 )
537     p_ret = TlsGetValue( p_tls->handle );
538
539 #elif defined(LIBVLC_USE_PTHREAD)
540     p_ret = pthread_getspecific( p_tls->handle );
541
542 #endif
543
544     return p_ret;
545 }
546
547 # if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0)
548 typedef struct
549 {
550     pthread_spinlock_t  spin;
551 } vlc_spinlock_t;
552
553 /**
554  * Initializes a spinlock.
555  */
556 static inline int vlc_spin_init (vlc_spinlock_t *spin)
557 {
558     return pthread_spin_init (&spin->spin, PTHREAD_PROCESS_PRIVATE);
559 }
560
561 /**
562  * Acquires a spinlock.
563  */
564 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
565 {
566     int val = pthread_spin_lock (&spin->spin);
567     assert (val == 0);
568 }
569
570 /**
571  * Releases a spinlock.
572  */
573 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
574 {
575     int val = pthread_spin_unlock (&spin->spin);
576     assert (val == 0);
577 }
578
579 /**
580  * Deinitializes a spinlock.
581  */
582 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
583 {
584     int val = pthread_spin_destroy (&spin->spin);
585     assert (val == 0);
586 }
587
588 #elif defined( WIN32 )
589
590 typedef CRITICAL_SECTION vlc_spinlock_t;
591
592 /**
593  * Initializes a spinlock.
594  */
595 static inline int vlc_spin_init (vlc_spinlock_t *spin)
596 {
597     return !InitializeCriticalSectionAndSpinCount(spin, 4000);
598 }
599
600 /**
601  * Acquires a spinlock.
602  */
603 static inline void vlc_spin_lock (vlc_spinlock_t *spin)
604 {
605     EnterCriticalSection(spin);
606 }
607
608 /**
609  * Releases a spinlock.
610  */
611 static inline void vlc_spin_unlock (vlc_spinlock_t *spin)
612 {
613     LeaveCriticalSection(spin);
614 }
615
616 /**
617  * Deinitializes a spinlock.
618  */
619 static inline void vlc_spin_destroy (vlc_spinlock_t *spin)
620 {
621     DeleteCriticalSection(spin);
622 }
623
624
625 #else
626
627 /* Fallback to plain mutexes if spinlocks are not available */
628 typedef vlc_mutex_t vlc_spinlock_t;
629
630 static inline int vlc_spin_init (vlc_spinlock_t *spin)
631 {
632     return __vlc_mutex_init (spin);
633 }
634
635 # define vlc_spin_lock    vlc_mutex_lock
636 # define vlc_spin_unlock  vlc_mutex_unlock
637 # define vlc_spin_destroy vlc_mutex_destroy
638 #endif
639
640 /*****************************************************************************
641  * vlc_thread_create: create a thread
642  *****************************************************************************/
643 #define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT )         \
644     __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, (void * ( * ) ( void * ))FUNC, PRIORITY, WAIT )
645
646 /*****************************************************************************
647  * vlc_thread_set_priority: set the priority of the calling thread
648  *****************************************************************************/
649 #define vlc_thread_set_priority( P_THIS, PRIORITY )                         \
650     __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY )
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__ )
663
664 #endif