1 /*****************************************************************************
2 * threads.c : threads implementation for the VideoLAN client
3 *****************************************************************************
4 * Copyright (C) 1999-2007 the VideoLAN team
7 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@netcourrier.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
32 #define VLC_THREADS_UNINITIALIZED 0
33 #define VLC_THREADS_PENDING 1
34 #define VLC_THREADS_ERROR 2
35 #define VLC_THREADS_READY 3
37 /*****************************************************************************
38 * Global mutex for lazy initialization of the threads system
39 *****************************************************************************/
40 static volatile unsigned i_initializations = 0;
41 static volatile int i_status = VLC_THREADS_UNINITIALIZED;
42 static vlc_object_t *p_root;
44 #if defined( PTH_INIT_IN_PTH_H )
45 #elif defined( ST_INIT_IN_ST_H )
46 #elif defined( UNDER_CE )
47 #elif defined( WIN32 )
48 static SIGNALOBJECTANDWAIT pf_SignalObjectAndWait = NULL;
49 static vlc_bool_t b_fast_mutex = 0;
50 static int i_win9x_cv = 0;
51 #elif defined( HAVE_KERNEL_SCHEDULER_H )
52 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
53 static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
54 #elif defined( HAVE_CTHREADS_H )
57 vlc_threadvar_t msg_context_global_key;
59 /*****************************************************************************
60 * vlc_threads_init: initialize threads system
61 *****************************************************************************
62 * This function requires lazy initialization of a global lock in order to
63 * keep the library really thread-safe. Some architectures don't support this
64 * and thus do not guarantee the complete reentrancy.
65 *****************************************************************************/
66 int __vlc_threads_init( vlc_object_t *p_this )
68 libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_this;
69 int i_ret = VLC_SUCCESS;
71 /* If we have lazy mutex initialization, use it. Otherwise, we just
72 * hope nothing wrong happens. */
73 #if defined( PTH_INIT_IN_PTH_H )
74 #elif defined( ST_INIT_IN_ST_H )
75 #elif defined( UNDER_CE )
76 #elif defined( WIN32 )
77 #elif defined( HAVE_KERNEL_SCHEDULER_H )
78 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
79 pthread_mutex_lock( &once_mutex );
80 #elif defined( HAVE_CTHREADS_H )
83 if( i_status == VLC_THREADS_UNINITIALIZED )
85 i_status = VLC_THREADS_PENDING;
87 /* We should be safe now. Do all the initialization stuff we want. */
88 p_libvlc_global->b_ready = VLC_FALSE;
90 #if defined( PTH_INIT_IN_PTH_H )
91 i_ret = ( pth_init() == FALSE );
93 #elif defined( ST_INIT_IN_ST_H )
96 #elif defined( UNDER_CE )
97 /* Nothing to initialize */
99 #elif defined( WIN32 )
100 /* Dynamically get the address of SignalObjectAndWait */
101 if( GetVersion() < 0x80000000 )
105 /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
106 hInstLib = LoadLibrary( "kernel32" );
109 pf_SignalObjectAndWait =
110 (SIGNALOBJECTANDWAIT)GetProcAddress( hInstLib,
111 "SignalObjectAndWait" );
115 #elif defined( HAVE_KERNEL_SCHEDULER_H )
116 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
117 #elif defined( HAVE_CTHREADS_H )
120 p_root = vlc_object_create( p_libvlc_global, VLC_OBJECT_GLOBAL );
126 i_status = VLC_THREADS_ERROR;
131 i_status = VLC_THREADS_READY;
134 vlc_threadvar_create( p_root, &msg_context_global_key );
138 /* Just increment the initialization count */
142 /* If we have lazy mutex initialization support, unlock the mutex;
143 * otherwize, do a naive wait loop. */
144 #if defined( PTH_INIT_IN_PTH_H )
145 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
146 #elif defined( ST_INIT_IN_ST_H )
147 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
148 #elif defined( UNDER_CE )
149 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
150 #elif defined( WIN32 )
151 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
152 #elif defined( HAVE_KERNEL_SCHEDULER_H )
153 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
154 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
155 pthread_mutex_unlock( &once_mutex );
156 #elif defined( HAVE_CTHREADS_H )
157 while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
160 if( i_status != VLC_THREADS_READY )
168 /*****************************************************************************
169 * vlc_threads_end: stop threads system
170 *****************************************************************************
171 * FIXME: This function is far from being threadsafe.
172 *****************************************************************************/
173 int __vlc_threads_end( vlc_object_t *p_this )
176 #if defined( PTH_INIT_IN_PTH_H )
177 #elif defined( ST_INIT_IN_ST_H )
178 #elif defined( UNDER_CE )
179 #elif defined( WIN32 )
180 #elif defined( HAVE_KERNEL_SCHEDULER_H )
181 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
182 pthread_mutex_lock( &once_mutex );
183 #elif defined( HAVE_CTHREADS_H )
186 if( i_initializations == 0 )
190 if( i_initializations == 0 )
192 i_status = VLC_THREADS_UNINITIALIZED;
193 vlc_object_destroy( p_root );
196 #if defined( PTH_INIT_IN_PTH_H )
197 if( i_initializations == 0 )
199 return ( pth_kill() == FALSE );
202 #elif defined( ST_INIT_IN_ST_H )
203 #elif defined( UNDER_CE )
204 #elif defined( WIN32 )
205 #elif defined( HAVE_KERNEL_SCHEDULER_H )
206 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
207 pthread_mutex_unlock( &once_mutex );
208 #elif defined( HAVE_CTHREADS_H )
213 /*****************************************************************************
214 * vlc_mutex_init: initialize a mutex
215 *****************************************************************************/
216 int __vlc_mutex_init( vlc_object_t *p_this, vlc_mutex_t *p_mutex )
218 p_mutex->p_this = p_this;
220 #if defined( PTH_INIT_IN_PTH_H )
221 return ( pth_mutex_init( &p_mutex->mutex ) == FALSE );
223 #elif defined( ST_INIT_IN_ST_H )
224 p_mutex->mutex = st_mutex_new();
225 return ( p_mutex->mutex == NULL ) ? errno : 0;
227 #elif defined( UNDER_CE )
228 InitializeCriticalSection( &p_mutex->csection );
231 #elif defined( WIN32 )
232 /* We use mutexes on WinNT/2K/XP because we can use the SignalObjectAndWait
233 * function and have a 100% correct vlc_cond_wait() implementation.
234 * As this function is not available on Win9x, we can use the faster
235 * CriticalSections */
236 if( pf_SignalObjectAndWait && !b_fast_mutex )
238 /* We are running on NT/2K/XP, we can use SignalObjectAndWait */
239 p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
240 return ( p_mutex->mutex != NULL ? 0 : 1 );
244 p_mutex->mutex = NULL;
245 InitializeCriticalSection( &p_mutex->csection );
249 #elif defined( HAVE_KERNEL_SCHEDULER_H )
250 /* check the arguments and whether it's already been initialized */
251 if( p_mutex == NULL )
256 if( p_mutex->init == 9999 )
261 p_mutex->lock = create_sem( 1, "BeMutex" );
262 if( p_mutex->lock < B_NO_ERROR )
267 p_mutex->init = 9999;
270 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
271 # if defined(DEBUG) && defined(SYS_LINUX)
273 /* Create error-checking mutex to detect problems more easily. */
274 pthread_mutexattr_t attr;
277 pthread_mutexattr_init( &attr );
278 pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
279 i_result = pthread_mutex_init( &p_mutex->mutex, &attr );
280 pthread_mutexattr_destroy( &attr );
284 return pthread_mutex_init( &p_mutex->mutex, NULL );
286 #elif defined( HAVE_CTHREADS_H )
287 mutex_init( p_mutex );
293 /*****************************************************************************
294 * vlc_mutex_destroy: destroy a mutex, inner version
295 *****************************************************************************/
296 int __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex )
299 /* In case of error : */
301 const char * psz_error = "";
303 #if defined( PTH_INIT_IN_PTH_H )
306 #elif defined( ST_INIT_IN_ST_H )
307 i_result = st_mutex_destroy( p_mutex->mutex );
309 #elif defined( UNDER_CE )
310 DeleteCriticalSection( &p_mutex->csection );
313 #elif defined( WIN32 )
316 CloseHandle( p_mutex->mutex );
320 DeleteCriticalSection( &p_mutex->csection );
324 #elif defined( HAVE_KERNEL_SCHEDULER_H )
325 if( p_mutex->init == 9999 )
327 delete_sem( p_mutex->lock );
333 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
334 i_result = pthread_mutex_destroy( &p_mutex->mutex );
337 i_thread = CAST_PTHREAD_TO_INT(pthread_self());
338 psz_error = strerror(i_result);
341 #elif defined( HAVE_CTHREADS_H )
348 msg_Err( p_mutex->p_this,
349 "thread %d: mutex_destroy failed at %s:%d (%d:%s)",
350 i_thread, psz_file, i_line, i_result, psz_error );
355 /*****************************************************************************
356 * vlc_cond_init: initialize a condition
357 *****************************************************************************/
358 int __vlc_cond_init( vlc_object_t *p_this, vlc_cond_t *p_condvar )
360 p_condvar->p_this = p_this;
362 #if defined( PTH_INIT_IN_PTH_H )
363 return ( pth_cond_init( &p_condvar->cond ) == FALSE );
365 #elif defined( ST_INIT_IN_ST_H )
366 p_condvar->cond = st_cond_new();
367 return ( p_condvar->cond == NULL ) ? errno : 0;
369 #elif defined( UNDER_CE )
370 /* Initialize counter */
371 p_condvar->i_waiting_threads = 0;
373 /* Create an auto-reset event. */
374 p_condvar->event = CreateEvent( NULL, /* no security */
375 FALSE, /* auto-reset event */
376 FALSE, /* start non-signaled */
377 NULL ); /* unnamed */
378 return !p_condvar->event;
380 #elif defined( WIN32 )
381 /* Initialize counter */
382 p_condvar->i_waiting_threads = 0;
385 p_condvar->i_win9x_cv = i_win9x_cv;
386 p_condvar->SignalObjectAndWait = pf_SignalObjectAndWait;
388 if( (p_condvar->SignalObjectAndWait && !b_fast_mutex)
389 || p_condvar->i_win9x_cv == 0 )
391 /* Create an auto-reset event. */
392 p_condvar->event = CreateEvent( NULL, /* no security */
393 FALSE, /* auto-reset event */
394 FALSE, /* start non-signaled */
395 NULL ); /* unnamed */
397 p_condvar->semaphore = NULL;
398 return !p_condvar->event;
402 p_condvar->semaphore = CreateSemaphore( NULL, /* no security */
403 0, /* initial count */
404 0x7fffffff, /* max count */
405 NULL ); /* unnamed */
407 if( p_condvar->i_win9x_cv == 1 )
408 /* Create a manual-reset event initially signaled. */
409 p_condvar->event = CreateEvent( NULL, TRUE, TRUE, NULL );
411 /* Create a auto-reset event. */
412 p_condvar->event = CreateEvent( NULL, FALSE, FALSE, NULL );
414 InitializeCriticalSection( &p_condvar->csection );
416 return !p_condvar->semaphore || !p_condvar->event;
419 #elif defined( HAVE_KERNEL_SCHEDULER_H )
425 if( p_condvar->init == 9999 )
430 p_condvar->thread = -1;
431 p_condvar->init = 9999;
434 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
435 return pthread_cond_init( &p_condvar->cond, NULL );
437 #elif defined( HAVE_CTHREADS_H )
438 /* condition_init() */
439 spin_lock_init( &p_condvar->lock );
440 cthread_queue_init( &p_condvar->queue );
442 p_condvar->implications = 0;
449 /*****************************************************************************
450 * vlc_cond_destroy: destroy a condition, inner version
451 *****************************************************************************/
452 int __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar )
455 /* In case of error : */
457 const char * psz_error = "";
459 #if defined( PTH_INIT_IN_PTH_H )
462 #elif defined( ST_INIT_IN_ST_H )
463 i_result = st_cond_destroy( p_condvar->cond );
465 #elif defined( UNDER_CE )
466 i_result = !CloseHandle( p_condvar->event );
468 #elif defined( WIN32 )
469 if( !p_condvar->semaphore )
470 i_result = !CloseHandle( p_condvar->event );
472 i_result = !CloseHandle( p_condvar->event )
473 || !CloseHandle( p_condvar->semaphore );
475 if( p_condvar->semaphore != NULL )
476 DeleteCriticalSection( &p_condvar->csection );
478 #elif defined( HAVE_KERNEL_SCHEDULER_H )
482 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
483 i_result = pthread_cond_destroy( &p_condvar->cond );
486 i_thread = CAST_PTHREAD_TO_INT(pthread_self());
487 psz_error = strerror(i_result);
490 #elif defined( HAVE_CTHREADS_H )
497 msg_Err( p_condvar->p_this,
498 "thread %d: cond_destroy failed at %s:%d (%d:%s)",
499 i_thread, psz_file, i_line, i_result, psz_error );
504 /*****************************************************************************
505 * vlc_tls_create: create a thread-local variable
506 *****************************************************************************/
507 int __vlc_threadvar_create( vlc_object_t *p_this, vlc_threadvar_t *p_tls )
511 #if defined( PTH_INIT_IN_PTH_H )
512 i_ret = pth_key_create( &p_tls->handle, NULL );
513 #elif defined( HAVE_KERNEL_SCHEDULER_H )
514 msg_Err( p_this, "TLS not implemented" );
516 #elif defined( ST_INIT_IN_ST_H )
517 i_ret = st_key_create( &p_tls->handle, NULL );
518 #elif defined( UNDER_CE ) || defined( WIN32 )
519 #elif defined( WIN32 )
520 p_tls->handle = TlsAlloc();
521 i_ret = !( p_tls->handle == 0xFFFFFFFF );
523 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
524 i_ret = pthread_key_create( &p_tls->handle, NULL );
525 #elif defined( HAVE_CTHREADS_H )
526 i_ret = cthread_keycreate( &p_tls-handle );
531 /*****************************************************************************
532 * vlc_thread_create: create a thread, inner version
533 *****************************************************************************
534 * Note that i_priority is only taken into account on platforms supporting
535 * userland real-time priority threads.
536 *****************************************************************************/
537 int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
538 const char *psz_name, void * ( *func ) ( void * ),
539 int i_priority, vlc_bool_t b_wait )
542 void *p_data = (void *)p_this;
544 vlc_mutex_lock( &p_this->object_lock );
546 #if defined( PTH_INIT_IN_PTH_H )
547 p_this->thread_id = pth_spawn( PTH_ATTR_DEFAULT, func, p_data );
548 i_ret = p_this->thread_id == NULL;
550 #elif defined( ST_INIT_IN_ST_H )
551 p_this->thread_id = st_thread_create( func, p_data, 1, 0 );
554 #elif defined( WIN32 ) || defined( UNDER_CE )
557 /* When using the MSVCRT C library you have to use the _beginthreadex
558 * function instead of CreateThread, otherwise you'll end up with
559 * memory leaks and the signal functions not working (see Microsoft
560 * Knowledge Base, article 104641) */
562 #if defined( UNDER_CE )
563 (HANDLE)CreateThread( NULL, 0, (PTHREAD_START) func,
564 p_data, 0, &threadID );
566 (HANDLE)_beginthreadex( NULL, 0, (PTHREAD_START) func,
567 p_data, 0, &threadID );
571 if( p_this->thread_id && i_priority )
573 if( !SetThreadPriority(p_this->thread_id, i_priority) )
575 msg_Warn( p_this, "couldn't set a faster priority" );
580 i_ret = ( p_this->thread_id ? 0 : 1 );
582 #elif defined( HAVE_KERNEL_SCHEDULER_H )
583 p_this->thread_id = spawn_thread( (thread_func)func, psz_name,
584 i_priority, p_data );
585 i_ret = resume_thread( p_this->thread_id );
587 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
588 i_ret = pthread_create( &p_this->thread_id, NULL, func, p_data );
591 if( config_GetInt( p_this, "rt-priority" ) )
594 int i_error, i_policy;
595 struct sched_param param;
597 memset( ¶m, 0, sizeof(struct sched_param) );
598 if( config_GetType( p_this, "rt-offset" ) )
600 i_priority += config_GetInt( p_this, "rt-offset" );
602 if( i_priority <= 0 )
604 param.sched_priority = (-1) * i_priority;
605 i_policy = SCHED_OTHER;
609 param.sched_priority = i_priority;
612 if( (i_error = pthread_setschedparam( p_this->thread_id,
613 i_policy, ¶m )) )
615 msg_Warn( p_this, "couldn't set thread priority (%s:%d): %s",
616 psz_file, i_line, strerror(i_error) );
627 #elif defined( HAVE_CTHREADS_H )
628 p_this->thread_id = cthread_fork( (cthread_fn_t)func, (any_t)p_data );
637 msg_Dbg( p_this, "waiting for thread completion" );
638 vlc_cond_wait( &p_this->object_wait, &p_this->object_lock );
641 p_this->b_thread = 1;
643 msg_Dbg( p_this, "thread %u (%s) created at priority %d (%s:%d)",
644 (unsigned int)p_this->thread_id, psz_name, i_priority,
647 vlc_mutex_unlock( &p_this->object_lock );
651 msg_Err( p_this, "%s thread could not be created at %s:%d (%s)",
652 psz_name, psz_file, i_line, strerror(i_ret) );
653 vlc_mutex_unlock( &p_this->object_lock );
659 /*****************************************************************************
660 * vlc_thread_set_priority: set the priority of the current thread when we
661 * couldn't set it in vlc_thread_create (for instance for the main thread)
662 *****************************************************************************/
663 int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
664 int i_line, int i_priority )
666 #if defined( PTH_INIT_IN_PTH_H ) || defined( ST_INIT_IN_ST_H )
667 #elif defined( WIN32 ) || defined( UNDER_CE )
668 if( !SetThreadPriority(GetCurrentThread(), i_priority) )
670 msg_Warn( p_this, "couldn't set a faster priority" );
674 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
676 if( config_GetInt( p_this, "rt-priority" ) )
679 int i_error, i_policy;
680 struct sched_param param;
682 memset( ¶m, 0, sizeof(struct sched_param) );
683 if( config_GetType( p_this, "rt-offset" ) )
685 i_priority += config_GetInt( p_this, "rt-offset" );
687 if( i_priority <= 0 )
689 param.sched_priority = (-1) * i_priority;
690 i_policy = SCHED_OTHER;
694 param.sched_priority = i_priority;
697 if( !p_this->thread_id )
698 p_this->thread_id = pthread_self();
699 if( (i_error = pthread_setschedparam( p_this->thread_id,
700 i_policy, ¶m )) )
702 msg_Warn( p_this, "couldn't set thread priority (%s:%d): %s",
703 psz_file, i_line, strerror(i_error) );
712 /*****************************************************************************
713 * vlc_thread_ready: tell the parent thread we were successfully spawned
714 *****************************************************************************/
715 void __vlc_thread_ready( vlc_object_t *p_this )
717 vlc_mutex_lock( &p_this->object_lock );
718 vlc_cond_signal( &p_this->object_wait );
719 vlc_mutex_unlock( &p_this->object_lock );
722 /*****************************************************************************
723 * vlc_thread_join: wait until a thread exits, inner version
724 *****************************************************************************/
725 void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line )
729 #if defined( PTH_INIT_IN_PTH_H )
730 i_ret = ( pth_join( p_this->thread_id, NULL ) == FALSE );
732 #elif defined( ST_INIT_IN_ST_H )
733 i_ret = st_thread_join( p_this->thread_id, NULL );
735 #elif defined( UNDER_CE ) || defined( WIN32 )
737 BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*,
738 FILETIME*, FILETIME* );
739 FILETIME create_ft, exit_ft, kernel_ft, user_ft;
740 int64_t real_time, kernel_time, user_time;
742 WaitForSingleObject( p_this->thread_id, INFINITE );
744 #if defined( UNDER_CE )
745 hmodule = GetModuleHandle( _T("COREDLL") );
747 hmodule = GetModuleHandle( _T("KERNEL32") );
749 OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*,
750 FILETIME*, FILETIME* ))
751 GetProcAddress( hmodule, _T("GetThreadTimes") );
753 if( OurGetThreadTimes &&
754 OurGetThreadTimes( p_this->thread_id,
755 &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
758 ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
759 ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
763 ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
764 kernel_ft.dwLowDateTime) / 10;
767 ((((int64_t)user_ft.dwHighDateTime)<<32)|
768 user_ft.dwLowDateTime) / 10;
770 msg_Dbg( p_this, "thread times: "
771 "real "I64Fd"m%fs, kernel "I64Fd"m%fs, user "I64Fd"m%fs",
772 real_time/60/1000000,
773 (double)((real_time%(60*1000000))/1000000.0),
774 kernel_time/60/1000000,
775 (double)((kernel_time%(60*1000000))/1000000.0),
776 user_time/60/1000000,
777 (double)((user_time%(60*1000000))/1000000.0) );
779 CloseHandle( p_this->thread_id );
781 #elif defined( HAVE_KERNEL_SCHEDULER_H )
783 wait_for_thread( p_this->thread_id, &exit_value );
785 #elif defined( PTHREAD_COND_T_IN_PTHREAD_H )
786 i_ret = pthread_join( p_this->thread_id, NULL );
788 #elif defined( HAVE_CTHREADS_H )
789 cthread_join( p_this->thread_id );
796 msg_Err( p_this, "thread_join(%u) failed at %s:%d (%s)",
797 (unsigned int)p_this->thread_id, psz_file, i_line,
802 msg_Dbg( p_this, "thread %u joined (%s:%d)",
803 (unsigned int)p_this->thread_id, psz_file, i_line );
806 p_this->b_thread = 0;