]> git.sesse.net Git - vlc/blob - src/misc/threads.c
Win32: don't use spin locks^W^Wfast mutexes instead of mutexes
[vlc] / src / misc / threads.c
1 /*****************************************************************************
2  * threads.c : threads implementation for the VideoLAN client
3  *****************************************************************************
4  * Copyright (C) 1999-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *          Clément Sténac
11  *
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.
16  *
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.
21  *
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  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc/vlc.h>
32
33 #include "libvlc.h"
34 #include <assert.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38
39 #define VLC_THREADS_UNINITIALIZED  0
40 #define VLC_THREADS_PENDING        1
41 #define VLC_THREADS_ERROR          2
42 #define VLC_THREADS_READY          3
43
44 /*****************************************************************************
45  * Global mutex for lazy initialization of the threads system
46  *****************************************************************************/
47 static volatile unsigned i_initializations = 0;
48 static volatile int i_status = VLC_THREADS_UNINITIALIZED;
49 static vlc_object_t *p_root;
50
51 #if defined( UNDER_CE )
52 #elif defined( WIN32 )
53
54 /*
55 ** On Windows 9x/Me you can use a fast but incorrect condition variables
56 ** implementation (more precisely there is a possibility for a race
57 ** condition to happen).
58 ** However it is possible to use slower alternatives which are more robust.
59 ** Currently you can choose between implementation 0 (which is the
60 ** fastest but slightly incorrect), 1 (default) and 2.
61 */
62 static int i_win9x_cv = 1;
63
64 #elif defined( HAVE_KERNEL_SCHEDULER_H )
65 #elif defined( LIBVLC_USE_PTHREAD )
66 static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
67 #endif
68
69 vlc_threadvar_t msg_context_global_key;
70
71 #if defined(LIBVLC_USE_PTHREAD)
72 static inline unsigned long vlc_threadid (void)
73 {
74      union { pthread_t th; unsigned long int i; } v = { };
75      v.th = pthread_self ();
76      return v.i;
77 }
78
79
80 /*****************************************************************************
81  * vlc_thread_fatal: Report an error from the threading layer
82  *****************************************************************************
83  * This is mostly meant for debugging.
84  *****************************************************************************/
85 void vlc_pthread_fatal (const char *action, int error,
86                         const char *file, unsigned line)
87 {
88     fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n",
89              action, vlc_threadid (), file, line, error);
90     fflush (stderr);
91
92     /* Sometimes strerror_r() crashes too, so make sure we print an error
93      * message before we invoke it */
94 #ifdef __GLIBC__
95     /* Avoid the strerror_r() prototype brain damage in glibc */
96     errno = error;
97     fprintf (stderr, " Error message: %m\n");
98 #else
99     char buf[1000];
100     const char *msg;
101
102     switch (strerror_r (error, buf, sizeof (buf)))
103     {
104         case 0:
105             msg = buf;
106             break;
107         case ERANGE: /* should never happen */
108             msg = "unknwon (too big to display)";
109             break;
110         default:
111             msg = "unknown (invalid error number)";
112             break;
113     }
114     fprintf (stderr, " Error message: %s\n", msg);
115 #endif
116
117     fflush (stderr);
118     abort ();
119 }
120 #endif
121
122
123 /*****************************************************************************
124  * vlc_threads_init: initialize threads system
125  *****************************************************************************
126  * This function requires lazy initialization of a global lock in order to
127  * keep the library really thread-safe. Some architectures don't support this
128  * and thus do not guarantee the complete reentrancy.
129  *****************************************************************************/
130 int __vlc_threads_init( vlc_object_t *p_this )
131 {
132     libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_this;
133     int i_ret = VLC_SUCCESS;
134
135     /* If we have lazy mutex initialization, use it. Otherwise, we just
136      * hope nothing wrong happens. */
137 #if defined( UNDER_CE )
138 #elif defined( WIN32 )
139     if( IsDebuggerPresent() )
140     {
141         /* SignalObjectAndWait() is problematic under a debugger */
142         i_win9x_cv = 1;
143     }
144 #elif defined( HAVE_KERNEL_SCHEDULER_H )
145 #elif defined( LIBVLC_USE_PTHREAD )
146     pthread_mutex_lock( &once_mutex );
147 #endif
148
149     if( i_status == VLC_THREADS_UNINITIALIZED )
150     {
151         i_status = VLC_THREADS_PENDING;
152
153         /* We should be safe now. Do all the initialization stuff we want. */
154         p_libvlc_global->b_ready = false;
155
156         p_root = vlc_custom_create( VLC_OBJECT(p_libvlc_global), 0,
157                                     VLC_OBJECT_GLOBAL, "global" );
158         if( p_root == NULL )
159             i_ret = VLC_ENOMEM;
160
161         if( i_ret )
162         {
163             i_status = VLC_THREADS_ERROR;
164         }
165         else
166         {
167             i_initializations++;
168             i_status = VLC_THREADS_READY;
169         }
170
171         vlc_threadvar_create( p_root, &msg_context_global_key );
172     }
173     else
174     {
175         /* Just increment the initialization count */
176         i_initializations++;
177     }
178
179     /* If we have lazy mutex initialization support, unlock the mutex;
180      * otherwize, do a naive wait loop. */
181 #if defined( UNDER_CE )
182     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
183 #elif defined( WIN32 )
184     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
185 #elif defined( HAVE_KERNEL_SCHEDULER_H )
186     while( i_status == VLC_THREADS_PENDING ) msleep( THREAD_SLEEP );
187 #elif defined( LIBVLC_USE_PTHREAD )
188     pthread_mutex_unlock( &once_mutex );
189 #endif
190
191     if( i_status != VLC_THREADS_READY )
192     {
193         return VLC_ETHREAD;
194     }
195
196     return i_ret;
197 }
198
199 /*****************************************************************************
200  * vlc_threads_end: stop threads system
201  *****************************************************************************
202  * FIXME: This function is far from being threadsafe.
203  *****************************************************************************/
204 int __vlc_threads_end( vlc_object_t *p_this )
205 {
206     (void)p_this;
207 #if defined( UNDER_CE )
208 #elif defined( WIN32 )
209 #elif defined( HAVE_KERNEL_SCHEDULER_H )
210 #elif defined( LIBVLC_USE_PTHREAD )
211     pthread_mutex_lock( &once_mutex );
212 #endif
213
214     if( i_initializations == 0 )
215         return VLC_EGENERIC;
216
217     i_initializations--;
218     if( i_initializations == 0 )
219     {
220         i_status = VLC_THREADS_UNINITIALIZED;
221         vlc_object_release( p_root );
222     }
223
224 #if defined( UNDER_CE )
225 #elif defined( WIN32 )
226 #elif defined( HAVE_KERNEL_SCHEDULER_H )
227 #elif defined( LIBVLC_USE_PTHREAD )
228     pthread_mutex_unlock( &once_mutex );
229 #endif
230     return VLC_SUCCESS;
231 }
232
233 #ifdef __linux__
234 /* This is not prototyped under Linux, though it exists. */
235 int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
236 #endif
237
238 /*****************************************************************************
239  * vlc_mutex_init: initialize a mutex
240  *****************************************************************************/
241 int __vlc_mutex_init( vlc_mutex_t *p_mutex )
242 {
243 #if defined( UNDER_CE )
244     InitializeCriticalSection( &p_mutex->csection );
245     return 0;
246
247 #elif defined( WIN32 )
248     p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
249     return ( p_mutex->mutex != NULL ? 0 : 1 );
250
251 #elif defined( HAVE_KERNEL_SCHEDULER_H )
252     /* check the arguments and whether it's already been initialized */
253     if( p_mutex == NULL )
254     {
255         return B_BAD_VALUE;
256     }
257
258     if( p_mutex->init == 9999 )
259     {
260         return EALREADY;
261     }
262
263     p_mutex->lock = create_sem( 1, "BeMutex" );
264     if( p_mutex->lock < B_NO_ERROR )
265     {
266         return( -1 );
267     }
268
269     p_mutex->init = 9999;
270     return B_OK;
271
272 #elif defined( LIBVLC_USE_PTHREAD )
273     pthread_mutexattr_t attr;
274     int                 i_result;
275
276     pthread_mutexattr_init( &attr );
277
278 # ifndef NDEBUG
279     /* Create error-checking mutex to detect problems more easily. */
280 #  if defined(SYS_LINUX)
281     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
282 #  else
283     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
284 #  endif
285 # endif
286     i_result = pthread_mutex_init( p_mutex, &attr );
287     pthread_mutexattr_destroy( &attr );
288     return i_result;
289 #endif
290 }
291
292 /*****************************************************************************
293  * vlc_mutex_init: initialize a recursive mutex (Do not use)
294  *****************************************************************************/
295 int __vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
296 {
297 #if defined( WIN32 )
298     /* Create mutex returns a recursive mutex */
299     p_mutex->mutex = CreateMutex( 0, FALSE, 0 );
300     return ( p_mutex->mutex != NULL ? 0 : 1 );
301 #elif defined( LIBVLC_USE_PTHREAD )
302     pthread_mutexattr_t attr;
303     int                 i_result;
304
305     pthread_mutexattr_init( &attr );
306 # ifndef NDEBUG
307     /* Create error-checking mutex to detect problems more easily. */
308 #   if defined(SYS_LINUX)
309     pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
310 #   else
311     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
312 #   endif
313 # endif
314     pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
315     i_result = pthread_mutex_init( p_mutex, &attr );
316     pthread_mutexattr_destroy( &attr );
317     return( i_result );
318 #else
319 # error Unimplemented!
320 #endif
321 }
322
323
324 /*****************************************************************************
325  * vlc_mutex_destroy: destroy a mutex, inner version
326  *****************************************************************************/
327 void __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex )
328 {
329 #if defined( UNDER_CE )
330     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
331
332     DeleteCriticalSection( &p_mutex->csection );
333
334 #elif defined( WIN32 )
335     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
336
337     if( p_mutex->mutex )
338         CloseHandle( p_mutex->mutex );
339     else
340         DeleteCriticalSection( &p_mutex->csection );
341
342 #elif defined( HAVE_KERNEL_SCHEDULER_H )
343     if( p_mutex->init == 9999 )
344         delete_sem( p_mutex->lock );
345
346     p_mutex->init = 0;
347
348 #elif defined( LIBVLC_USE_PTHREAD )
349     int val = pthread_mutex_destroy( p_mutex );
350     VLC_THREAD_ASSERT ("destroying mutex");
351
352 #endif
353 }
354
355 /*****************************************************************************
356  * vlc_cond_init: initialize a condition
357  *****************************************************************************/
358 int __vlc_cond_init( vlc_cond_t *p_condvar )
359 {
360 #if defined( UNDER_CE )
361     /* Initialize counter */
362     p_condvar->i_waiting_threads = 0;
363
364     /* Create an auto-reset event. */
365     p_condvar->event = CreateEvent( NULL,   /* no security */
366                                     FALSE,  /* auto-reset event */
367                                     FALSE,  /* start non-signaled */
368                                     NULL ); /* unnamed */
369     return !p_condvar->event;
370
371 #elif defined( WIN32 )
372     /* Initialize counter */
373     p_condvar->i_waiting_threads = 0;
374
375     /* Misc init */
376     p_condvar->i_win9x_cv = i_win9x_cv;
377
378     /* Create an auto-reset event. */
379     p_condvar->event = CreateEvent( NULL,   /* no security */
380                                     FALSE,  /* auto-reset event */
381                                     FALSE,  /* start non-signaled */
382                                     NULL ); /* unnamed */
383
384     p_condvar->semaphore = NULL;
385     return !p_condvar->event;
386
387 #elif defined( HAVE_KERNEL_SCHEDULER_H )
388     if( !p_condvar )
389     {
390         return B_BAD_VALUE;
391     }
392
393     if( p_condvar->init == 9999 )
394     {
395         return EALREADY;
396     }
397
398     p_condvar->thread = -1;
399     p_condvar->init = 9999;
400     return 0;
401
402 #elif defined( LIBVLC_USE_PTHREAD )
403     pthread_condattr_t attr;
404     int ret;
405
406     ret = pthread_condattr_init (&attr);
407     if (ret)
408         return ret;
409
410 # if !defined (_POSIX_CLOCK_SELECTION)
411    /* Fairly outdated POSIX support (that was defined in 2001) */
412 #  define _POSIX_CLOCK_SELECTION (-1)
413 # endif
414 # if (_POSIX_CLOCK_SELECTION >= 0)
415     /* NOTE: This must be the same clock as the one in mtime.c */
416     pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
417 # endif
418
419     ret = pthread_cond_init (p_condvar, &attr);
420     pthread_condattr_destroy (&attr);
421     return ret;
422
423 #endif
424 }
425
426 /*****************************************************************************
427  * vlc_cond_destroy: destroy a condition, inner version
428  *****************************************************************************/
429 void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar )
430 {
431 #if defined( UNDER_CE )
432     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
433
434     CloseHandle( p_condvar->event );
435
436 #elif defined( WIN32 )
437     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
438
439     if( !p_condvar->semaphore )
440         CloseHandle( p_condvar->event );
441     else
442     {
443         CloseHandle( p_condvar->event );
444         CloseHandle( p_condvar->semaphore );
445     }
446
447     if( p_condvar->semaphore != NULL )
448         DeleteCriticalSection( &p_condvar->csection );
449
450 #elif defined( HAVE_KERNEL_SCHEDULER_H )
451     p_condvar->init = 0;
452
453 #elif defined( LIBVLC_USE_PTHREAD )
454     int val = pthread_cond_destroy( p_condvar );
455     VLC_THREAD_ASSERT ("destroying condition");
456
457 #endif
458 }
459
460 /*****************************************************************************
461  * vlc_tls_create: create a thread-local variable
462  *****************************************************************************/
463 int __vlc_threadvar_create( vlc_threadvar_t *p_tls )
464 {
465     int i_ret = -1;
466
467 #if defined( HAVE_KERNEL_SCHEDULER_H )
468 # error Unimplemented!
469 #elif defined( UNDER_CE ) || defined( WIN32 )
470 #elif defined( WIN32 )
471     *p_tls = TlsAlloc();
472     i_ret = (*p_tls == INVALID_HANDLE_VALUE) ? EAGAIN : 0;
473
474 #elif defined( LIBVLC_USE_PTHREAD )
475     i_ret =  pthread_key_create( p_tls, NULL );
476 #endif
477     return i_ret;
478 }
479
480 /*****************************************************************************
481  * vlc_thread_create: create a thread, inner version
482  *****************************************************************************
483  * Note that i_priority is only taken into account on platforms supporting
484  * userland real-time priority threads.
485  *****************************************************************************/
486 int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
487                          const char *psz_name, void * ( *func ) ( void * ),
488                          int i_priority, bool b_wait )
489 {
490     int i_ret;
491     void *p_data = (void *)p_this;
492     vlc_object_internals_t *p_priv = p_this->p_internals;
493
494     vlc_mutex_lock( &p_this->object_lock );
495
496 #if defined( WIN32 ) || defined( UNDER_CE )
497     {
498         /* When using the MSVCRT C library you have to use the _beginthreadex
499          * function instead of CreateThread, otherwise you'll end up with
500          * memory leaks and the signal functions not working (see Microsoft
501          * Knowledge Base, article 104641) */
502 #if defined( UNDER_CE )
503         DWORD  threadId;
504         HANDLE hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)func,
505                                       (LPVOID)p_data, CREATE_SUSPENDED,
506                       &threadId );
507 #else
508         unsigned threadId;
509         uintptr_t hThread = _beginthreadex( NULL, 0,
510                         (LPTHREAD_START_ROUTINE)func,
511                                             (void*)p_data, CREATE_SUSPENDED,
512                         &threadId );
513 #endif
514         p_priv->thread_id.id = (DWORD)threadId;
515         p_priv->thread_id.hThread = (HANDLE)hThread;
516         ResumeThread((HANDLE)hThread);
517     }
518
519     i_ret = ( p_priv->thread_id.hThread ? 0 : 1 );
520
521     if( !i_ret && i_priority )
522     {
523         if( !SetThreadPriority(p_priv->thread_id.hThread, i_priority) )
524         {
525             msg_Warn( p_this, "couldn't set a faster priority" );
526             i_priority = 0;
527         }
528     }
529
530 #elif defined( HAVE_KERNEL_SCHEDULER_H )
531     p_priv->thread_id = spawn_thread( (thread_func)func, psz_name,
532                                       i_priority, p_data );
533     i_ret = resume_thread( p_priv->thread_id );
534
535 #elif defined( LIBVLC_USE_PTHREAD )
536     i_ret = pthread_create( &p_priv->thread_id, NULL, func, p_data );
537
538 #ifndef __APPLE__
539     if( config_GetInt( p_this, "rt-priority" ) )
540 #endif
541     {
542         int i_error, i_policy;
543         struct sched_param param;
544
545         memset( &param, 0, sizeof(struct sched_param) );
546         if( config_GetType( p_this, "rt-offset" ) )
547         {
548             i_priority += config_GetInt( p_this, "rt-offset" );
549         }
550         if( i_priority <= 0 )
551         {
552             param.sched_priority = (-1) * i_priority;
553             i_policy = SCHED_OTHER;
554         }
555         else
556         {
557             param.sched_priority = i_priority;
558             i_policy = SCHED_RR;
559         }
560         if( (i_error = pthread_setschedparam( p_priv->thread_id,
561                                                i_policy, &param )) )
562         {
563             errno = i_error;
564             msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
565                       psz_file, i_line );
566             i_priority = 0;
567         }
568     }
569 #ifndef __APPLE__
570     else
571     {
572         i_priority = 0;
573     }
574 #endif
575
576 #endif
577
578     if( i_ret == 0 )
579     {
580         if( b_wait )
581         {
582             msg_Dbg( p_this, "waiting for thread completion" );
583             vlc_object_wait( p_this );
584         }
585
586         p_priv->b_thread = true;
587
588 #if defined( WIN32 ) || defined( UNDER_CE )
589         msg_Dbg( p_this, "thread %u (%s) created at priority %d (%s:%d)",
590                  (unsigned int)p_priv->thread_id.id, psz_name,
591          i_priority, psz_file, i_line );
592 #else
593         msg_Dbg( p_this, "thread %u (%s) created at priority %d (%s:%d)",
594                  (unsigned int)p_priv->thread_id, psz_name, i_priority,
595                  psz_file, i_line );
596 #endif
597
598
599         vlc_mutex_unlock( &p_this->object_lock );
600     }
601     else
602     {
603         errno = i_ret;
604         msg_Err( p_this, "%s thread could not be created at %s:%d (%m)",
605                          psz_name, psz_file, i_line );
606         vlc_mutex_unlock( &p_this->object_lock );
607     }
608
609     return i_ret;
610 }
611
612 /*****************************************************************************
613  * vlc_thread_set_priority: set the priority of the current thread when we
614  * couldn't set it in vlc_thread_create (for instance for the main thread)
615  *****************************************************************************/
616 int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
617                                int i_line, int i_priority )
618 {
619     vlc_object_internals_t *p_priv = p_this->p_internals;
620 #if defined( WIN32 ) || defined( UNDER_CE )
621     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
622
623     if( !p_priv->thread_id.hThread )
624         p_priv->thread_id.hThread = GetCurrentThread();
625     if( !SetThreadPriority(p_priv->thread_id.hThread, i_priority) )
626     {
627         msg_Warn( p_this, "couldn't set a faster priority" );
628         return 1;
629     }
630
631 #elif defined( LIBVLC_USE_PTHREAD )
632 # ifndef __APPLE__
633     if( config_GetInt( p_this, "rt-priority" ) > 0 )
634 # endif
635     {
636         int i_error, i_policy;
637         struct sched_param param;
638
639         memset( &param, 0, sizeof(struct sched_param) );
640         if( config_GetType( p_this, "rt-offset" ) )
641         {
642             i_priority += config_GetInt( p_this, "rt-offset" );
643         }
644         if( i_priority <= 0 )
645         {
646             param.sched_priority = (-1) * i_priority;
647             i_policy = SCHED_OTHER;
648         }
649         else
650         {
651             param.sched_priority = i_priority;
652             i_policy = SCHED_RR;
653         }
654         if( !p_priv->thread_id )
655             p_priv->thread_id = pthread_self();
656         if( (i_error = pthread_setschedparam( p_priv->thread_id,
657                                                i_policy, &param )) )
658         {
659             errno = i_error;
660             msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
661                       psz_file, i_line );
662             i_priority = 0;
663         }
664     }
665 #endif
666
667     return 0;
668 }
669
670 /*****************************************************************************
671  * vlc_thread_ready: tell the parent thread we were successfully spawned
672  *****************************************************************************/
673 void __vlc_thread_ready( vlc_object_t *p_this )
674 {
675     vlc_object_signal( p_this );
676 }
677
678 /*****************************************************************************
679  * vlc_thread_join: wait until a thread exits, inner version
680  *****************************************************************************/
681 void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line )
682 {
683     vlc_object_internals_t *p_priv = p_this->p_internals;
684
685 #if defined( UNDER_CE ) || defined( WIN32 )
686     HMODULE hmodule;
687     BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*,
688                                       FILETIME*, FILETIME* );
689     FILETIME create_ft, exit_ft, kernel_ft, user_ft;
690     int64_t real_time, kernel_time, user_time;
691     HANDLE hThread;
692
693     /*
694     ** object will close its thread handle when destroyed, duplicate it here
695     ** to be on the safe side
696     */
697     if( ! DuplicateHandle(GetCurrentProcess(),
698             p_priv->thread_id.hThread,
699             GetCurrentProcess(),
700             &hThread,
701             0,
702             FALSE,
703             DUPLICATE_SAME_ACCESS) )
704     {
705         msg_Err( p_this, "thread_join(%u) failed at %s:%d (%u)",
706                          (unsigned int)p_priv->thread_id.id,
707              psz_file, i_line, (unsigned int)GetLastError() );
708         p_priv->b_thread = false;
709         return;
710     }
711
712     WaitForSingleObject( hThread, INFINITE );
713
714     msg_Dbg( p_this, "thread %u joined (%s:%d)",
715              (unsigned int)p_priv->thread_id.id,
716              psz_file, i_line );
717 #if defined( UNDER_CE )
718     hmodule = GetModuleHandle( _T("COREDLL") );
719 #else
720     hmodule = GetModuleHandle( _T("KERNEL32") );
721 #endif
722     OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*,
723                                          FILETIME*, FILETIME* ))
724         GetProcAddress( hmodule, _T("GetThreadTimes") );
725
726     if( OurGetThreadTimes &&
727         OurGetThreadTimes( hThread,
728                            &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
729     {
730         real_time =
731           ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
732           ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
733         real_time /= 10;
734
735         kernel_time =
736           ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
737            kernel_ft.dwLowDateTime) / 10;
738
739         user_time =
740           ((((int64_t)user_ft.dwHighDateTime)<<32)|
741            user_ft.dwLowDateTime) / 10;
742
743         msg_Dbg( p_this, "thread times: "
744                  "real "I64Fd"m%fs, kernel "I64Fd"m%fs, user "I64Fd"m%fs",
745                  real_time/60/1000000,
746                  (double)((real_time%(60*1000000))/1000000.0),
747                  kernel_time/60/1000000,
748                  (double)((kernel_time%(60*1000000))/1000000.0),
749                  user_time/60/1000000,
750                  (double)((user_time%(60*1000000))/1000000.0) );
751     }
752     CloseHandle( hThread );
753
754 #else /* !defined(WIN32) */
755
756     int i_ret = 0;
757
758 #if defined( HAVE_KERNEL_SCHEDULER_H )
759     int32_t exit_value;
760     i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value ));
761
762 #elif defined( LIBVLC_USE_PTHREAD )
763     /* Make sure we do return if we are calling vlc_thread_join()
764      * from the joined thread */
765     if (pthread_equal (pthread_self (), p_priv->thread_id))
766         i_ret = pthread_detach (p_priv->thread_id);
767     else
768         i_ret = pthread_join (p_priv->thread_id, NULL);
769 #endif
770
771     if( i_ret )
772     {
773         errno = i_ret;
774         msg_Err( p_this, "thread_join(%u) failed at %s:%d (%m)",
775                          (unsigned int)p_priv->thread_id, psz_file, i_line );
776     }
777     else
778     {
779         msg_Dbg( p_this, "thread %u joined (%s:%d)",
780                          (unsigned int)p_priv->thread_id, psz_file, i_line );
781     }
782
783 #endif
784
785     p_priv->b_thread = false;
786 }
787