threads.c 21.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * threads.c : threads implementation for the VideoLAN client
 *****************************************************************************
4
 * Copyright (C) 1999-2007 the VideoLAN team
5
 * $Id$
6 7 8 9
 *
 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
 *          Gildas Bazin <gbazin@netcourrier.com>
10
 *          Clément Sténac
11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
16
 *
17 18 19 20 21 22 23
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26
 *****************************************************************************/

27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31 32
#include <vlc/vlc.h>

33
#include "libvlc.h"
34
#include <assert.h>
35 36 37
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
38

39 40 41 42 43 44
#define VLC_THREADS_UNINITIALIZED  0
#define VLC_THREADS_PENDING        1
#define VLC_THREADS_ERROR          2
#define VLC_THREADS_READY          3

/*****************************************************************************
45
 * Global mutex for lazy initialization of the threads system
46
 *****************************************************************************/
47
static volatile unsigned i_initializations = 0;
48

49
#if defined( LIBVLC_USE_PTHREAD )
50
static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
51 52
#endif

53 54 55 56
/**
 * Global process-wide VLC object.
 * Contains inter-instance data, such as the module cache and global mutexes.
 */
57
static libvlc_global_data_t *p_root;
58 59 60 61

libvlc_global_data_t *vlc_global( void )
{
    assert( i_initializations > 0 );
62
    return p_root;
63 64 65
}


66 67
vlc_threadvar_t msg_context_global_key;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
68 69 70 71 72 73 74 75
#if defined(LIBVLC_USE_PTHREAD)
static inline unsigned long vlc_threadid (void)
{
     union { pthread_t th; unsigned long int i; } v = { };
     v.th = pthread_self ();
     return v.i;
}

76
/*****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
77
 * vlc_thread_fatal: Report an error from the threading layer
78
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
79
 * This is mostly meant for debugging.
80
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
81 82
void vlc_pthread_fatal (const char *action, int error,
                        const char *file, unsigned line)
83
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
84 85 86 87
    fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n",
             action, vlc_threadid (), file, line, error);
    fflush (stderr);

88
    /* Sometimes strerror_r() crashes too, so make sure we print an error
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
89
     * message before we invoke it */
90 91 92 93
#ifdef __GLIBC__
    /* Avoid the strerror_r() prototype brain damage in glibc */
    errno = error;
    fprintf (stderr, " Error message: %m\n");
94
#else
95 96 97
    char buf[1000];
    const char *msg;

Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
98
    switch (strerror_r (error, buf, sizeof (buf)))
99 100 101 102 103 104 105 106 107 108 109 110
    {
        case 0:
            msg = buf;
            break;
        case ERANGE: /* should never happen */
            msg = "unknwon (too big to display)";
            break;
        default:
            msg = "unknown (invalid error number)";
            break;
    }
    fprintf (stderr, " Error message: %s\n", msg);
111
#endif
112

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
113 114
    fflush (stderr);
    abort ();
115
}
116 117 118 119 120 121 122
#else
void vlc_pthread_fatal (const char *action, int error,
                        const char *file, unsigned line)
{
    (void)action; (void)error; (void)file; (void)line;
    abort();
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
123
#endif
124

125 126
/*****************************************************************************
 * vlc_threads_init: initialize threads system
127 128 129 130
 *****************************************************************************
 * This function requires lazy initialization of a global lock in order to
 * keep the library really thread-safe. Some architectures don't support this
 * and thus do not guarantee the complete reentrancy.
131
 *****************************************************************************/
132
int vlc_threads_init( void )
133
{
134 135 136 137
    int i_ret = VLC_SUCCESS;

    /* If we have lazy mutex initialization, use it. Otherwise, we just
     * hope nothing wrong happens. */
138
#if defined( LIBVLC_USE_PTHREAD )
139 140
    pthread_mutex_lock( &once_mutex );
#endif
141

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
142
    if( i_initializations == 0 )
143
    {
144
        p_root = vlc_custom_create( NULL, sizeof( *p_root ),
145
                                    VLC_OBJECT_GENERIC, "root" );
146
        if( p_root == NULL )
147
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
148 149
            i_ret = VLC_ENOMEM;
            goto out;
150
        }
151 152

        /* We should be safe now. Do all the initialization stuff we want. */
153
        vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy );
154
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
155
    i_initializations++;
156

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
157 158 159
out:
    /* If we have lazy mutex initialization support, unlock the mutex.
     * Otherwize, we are screwed. */
160
#if defined( LIBVLC_USE_PTHREAD )
161 162
    pthread_mutex_unlock( &once_mutex );
#endif
163

164
    return i_ret;
165 166 167 168
}

/*****************************************************************************
 * vlc_threads_end: stop threads system
169
 *****************************************************************************
170
 * FIXME: This function is far from being threadsafe.
171
 *****************************************************************************/
172
void vlc_threads_end( void )
173
{
174
#if defined( LIBVLC_USE_PTHREAD )
175 176 177
    pthread_mutex_lock( &once_mutex );
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
178
    assert( i_initializations > 0 );
179 180

    if( i_initializations == 1 )
181
    {
182
        vlc_object_release( p_root );
183 184
        vlc_threadvar_delete( &msg_context_global_key );
    }
185
    i_initializations--;
186

187
#if defined( LIBVLC_USE_PTHREAD )
188
    pthread_mutex_unlock( &once_mutex );
189 190 191
#endif
}

192 193
#if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
/* This is not prototyped under glibc, though it exists. */
194 195 196
int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind );
#endif

197 198 199
/*****************************************************************************
 * vlc_mutex_init: initialize a mutex
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
200
int vlc_mutex_init( vlc_mutex_t *p_mutex )
201
{
202 203 204 205 206 207 208 209
#if defined( LIBVLC_USE_PTHREAD )
    pthread_mutexattr_t attr;
    int                 i_result;

    pthread_mutexattr_init( &attr );

# ifndef NDEBUG
    /* Create error-checking mutex to detect problems more easily. */
210
#  if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
211 212 213 214 215 216 217 218 219
    pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
#  else
    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
#  endif
# endif
    i_result = pthread_mutex_init( p_mutex, &attr );
    pthread_mutexattr_destroy( &attr );
    return i_result;
#elif defined( UNDER_CE )
220 221 222
    InitializeCriticalSection( &p_mutex->csection );
    return 0;

223
#elif defined( WIN32 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
224 225
    *p_mutex = CreateMutex( 0, FALSE, 0 );
    return (*p_mutex != NULL) ? 0 : ENOMEM;
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250

#elif defined( HAVE_KERNEL_SCHEDULER_H )
    /* check the arguments and whether it's already been initialized */
    if( p_mutex == NULL )
    {
        return B_BAD_VALUE;
    }

    if( p_mutex->init == 9999 )
    {
        return EALREADY;
    }

    p_mutex->lock = create_sem( 1, "BeMutex" );
    if( p_mutex->lock < B_NO_ERROR )
    {
        return( -1 );
    }

    p_mutex->init = 9999;
    return B_OK;

#endif
}

251 252 253
/*****************************************************************************
 * vlc_mutex_init: initialize a recursive mutex (Do not use)
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254
int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
255
{
256
#if defined( LIBVLC_USE_PTHREAD )
257 258 259 260
    pthread_mutexattr_t attr;
    int                 i_result;

    pthread_mutexattr_init( &attr );
261 262 263
#  if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6)
    pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_RECURSIVE_NP );
#  else
264
    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
265
#  endif
266
    i_result = pthread_mutex_init( p_mutex, &attr );
267 268
    pthread_mutexattr_destroy( &attr );
    return( i_result );
269 270 271 272
#elif defined( WIN32 )
    /* Create mutex returns a recursive mutex */
    *p_mutex = CreateMutex( 0, FALSE, 0 );
    return (*p_mutex != NULL) ? 0 : ENOMEM;
273
#else
274
# error Unimplemented!
275 276 277 278
#endif
}


279 280 281
/*****************************************************************************
 * vlc_mutex_destroy: destroy a mutex, inner version
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
282
void __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex )
283
{
284 285 286 287 288
#if defined( LIBVLC_USE_PTHREAD )
    int val = pthread_mutex_destroy( p_mutex );
    VLC_THREAD_ASSERT ("destroying mutex");

#elif defined( UNDER_CE )
289 290
    VLC_UNUSED( psz_file); VLC_UNUSED( i_line );

291 292
    DeleteCriticalSection( &p_mutex->csection );

293
#elif defined( WIN32 )
294 295
    VLC_UNUSED( psz_file); VLC_UNUSED( i_line );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
296
    CloseHandle( *p_mutex );
297

298 299 300 301 302 303
#elif defined( HAVE_KERNEL_SCHEDULER_H )
    if( p_mutex->init == 9999 )
        delete_sem( p_mutex->lock );

    p_mutex->init = 0;

304
#endif
305 306 307 308 309
}

/*****************************************************************************
 * vlc_cond_init: initialize a condition
 *****************************************************************************/
310
int __vlc_cond_init( vlc_cond_t *p_condvar )
311
{
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
#if defined( LIBVLC_USE_PTHREAD )
    pthread_condattr_t attr;
    int ret;

    ret = pthread_condattr_init (&attr);
    if (ret)
        return ret;

# if !defined (_POSIX_CLOCK_SELECTION)
   /* Fairly outdated POSIX support (that was defined in 2001) */
#  define _POSIX_CLOCK_SELECTION (-1)
# endif
# if (_POSIX_CLOCK_SELECTION >= 0)
    /* NOTE: This must be the same clock as the one in mtime.c */
    pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
# endif

    ret = pthread_cond_init (p_condvar, &attr);
    pthread_condattr_destroy (&attr);
    return ret;

#elif defined( UNDER_CE ) || defined( WIN32 )
334
    /* Initialize counter */
335 336
    p_condvar->i_waiting_threads = 0;

337 338 339 340 341 342
    /* Create an auto-reset event. */
    p_condvar->event = CreateEvent( NULL,   /* no security */
                                    FALSE,  /* auto-reset event */
                                    FALSE,  /* start non-signaled */
                                    NULL ); /* unnamed */
    return !p_condvar->event;
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

#elif defined( HAVE_KERNEL_SCHEDULER_H )
    if( !p_condvar )
    {
        return B_BAD_VALUE;
    }

    if( p_condvar->init == 9999 )
    {
        return EALREADY;
    }

    p_condvar->thread = -1;
    p_condvar->init = 9999;
    return 0;
358

359 360 361 362 363 364
#endif
}

/*****************************************************************************
 * vlc_cond_destroy: destroy a condition, inner version
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
365
void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar )
366
{
367 368 369 370 371
#if defined( LIBVLC_USE_PTHREAD )
    int val = pthread_cond_destroy( p_condvar );
    VLC_THREAD_ASSERT ("destroying condition");

#elif defined( UNDER_CE ) || defined( WIN32 )
372 373
    VLC_UNUSED( psz_file); VLC_UNUSED( i_line );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
374
    CloseHandle( p_condvar->event );
375

376 377 378
#elif defined( HAVE_KERNEL_SCHEDULER_H )
    p_condvar->init = 0;

379
#endif
380 381
}

382 383 384
/*****************************************************************************
 * vlc_tls_create: create a thread-local variable
 *****************************************************************************/
385
int vlc_threadvar_create( vlc_threadvar_t *p_tls, void (*destr) (void *) )
386
{
387
    int i_ret;
388

389
#if defined( LIBVLC_USE_PTHREAD )
390
    i_ret =  pthread_key_create( p_tls, destr );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
391
#elif defined( UNDER_CE )
392
    i_ret = ENOSYS;
393
#elif defined( WIN32 )
394
    *p_tls = TlsAlloc();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
395
    i_ret = (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
396 397
#else
# error Unimplemented!
398
#endif
399
    return i_ret;
400 401
}

402 403 404
void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
#if defined( LIBVLC_USE_PTHREAD )
405
    pthread_key_delete (*p_tls);
406 407 408 409 410 411 412 413
#elif defined( UNDER_CE )
#elif defined( WIN32 )
    TlsFree (*p_tls);
#else
# error Unimplemented!
#endif
}

414 415
/*****************************************************************************
 * vlc_thread_create: create a thread, inner version
416 417 418
 *****************************************************************************
 * Note that i_priority is only taken into account on platforms supporting
 * userland real-time priority threads.
419
 *****************************************************************************/
420 421
int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
                         const char *psz_name, void * ( *func ) ( void * ),
422
                         int i_priority, bool b_wait )
423 424
{
    int i_ret;
425
    void *p_data = (void *)p_this;
426
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
427

Sam Hocevar's avatar
Sam Hocevar committed
428
    vlc_mutex_lock( &p_this->object_lock );
429

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
#if defined( LIBVLC_USE_PTHREAD )
    i_ret = pthread_create( &p_priv->thread_id, NULL, func, p_data );

#ifndef __APPLE__
    if( config_GetInt( p_this, "rt-priority" ) > 0 )
#endif
    {
        int i_error, i_policy;
        struct sched_param param;

        memset( &param, 0, sizeof(struct sched_param) );
        if( config_GetType( p_this, "rt-offset" ) )
            i_priority += config_GetInt( p_this, "rt-offset" );
        if( i_priority <= 0 )
        {
            param.sched_priority = (-1) * i_priority;
            i_policy = SCHED_OTHER;
        }
        else
        {
            param.sched_priority = i_priority;
            i_policy = SCHED_RR;
        }
        if( (i_error = pthread_setschedparam( p_priv->thread_id,
                                               i_policy, &param )) )
        {
            errno = i_error;
            msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
                      psz_file, i_line );
            i_priority = 0;
        }
    }
#ifndef __APPLE__
    else
        i_priority = 0;
#endif

#elif defined( WIN32 ) || defined( UNDER_CE )
468 469
    {
        /* When using the MSVCRT C library you have to use the _beginthreadex
Gildas Bazin's avatar
 
Gildas Bazin committed
470
         * function instead of CreateThread, otherwise you'll end up with
471 472 473
         * memory leaks and the signal functions not working (see Microsoft
         * Knowledge Base, article 104641) */
#if defined( UNDER_CE )
474
        HANDLE hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)func,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
475 476
                                       (LPVOID)p_data, CREATE_SUSPENDED,
                                        NULL );
477
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
478 479 480
        HANDLE hThread = (HANDLE)(uintptr_t)
            _beginthreadex( NULL, 0, (LPTHREAD_START_ROUTINE)func,
                            (void *)p_data, CREATE_SUSPENDED, NULL );
481
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
482 483
        p_priv->thread_id = hThread;
        ResumeThread(hThread);
484
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
485

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
486
    i_ret = ( p_priv->thread_id ? 0 : errno );
487

488
    if( !i_ret && i_priority )
489
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
490
        if( !SetThreadPriority(p_priv->thread_id, i_priority) )
491 492 493 494 495 496
        {
            msg_Warn( p_this, "couldn't set a faster priority" );
            i_priority = 0;
        }
    }

497
#elif defined( HAVE_KERNEL_SCHEDULER_H )
498
    p_priv->thread_id = spawn_thread( (thread_func)func, psz_name,
499
                                      i_priority, p_data );
500
    i_ret = resume_thread( p_priv->thread_id );
501

502 503 504 505 506 507 508
#endif

    if( i_ret == 0 )
    {
        if( b_wait )
        {
            msg_Dbg( p_this, "waiting for thread completion" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
509
            vlc_object_wait( p_this );
510 511
        }

512
        p_priv->b_thread = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
513 514
        msg_Dbg( p_this, "thread %lu (%s) created at priority %d (%s:%d)",
                 (unsigned long)p_priv->thread_id, psz_name, i_priority,
515
                 psz_file, i_line );
516 517 518
    }
    else
    {
519 520 521
        errno = i_ret;
        msg_Err( p_this, "%s thread could not be created at %s:%d (%m)",
                         psz_name, psz_file, i_line );
522 523
    }

524
    vlc_mutex_unlock( &p_this->object_lock );
525 526 527
    return i_ret;
}

528 529 530 531
/*****************************************************************************
 * vlc_thread_set_priority: set the priority of the current thread when we
 * couldn't set it in vlc_thread_create (for instance for the main thread)
 *****************************************************************************/
532
int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
533 534
                               int i_line, int i_priority )
{
535
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
536

537
#if defined( LIBVLC_USE_PTHREAD )
538 539 540
# ifndef __APPLE__
    if( config_GetInt( p_this, "rt-priority" ) > 0 )
# endif
541
    {
542
        int i_error, i_policy;
543
        struct sched_param param;
544

545
        memset( &param, 0, sizeof(struct sched_param) );
546 547
        if( config_GetType( p_this, "rt-offset" ) )
            i_priority += config_GetInt( p_this, "rt-offset" );
548
        if( i_priority <= 0 )
549 550 551 552 553 554 555 556 557
        {
            param.sched_priority = (-1) * i_priority;
            i_policy = SCHED_OTHER;
        }
        else
        {
            param.sched_priority = i_priority;
            i_policy = SCHED_RR;
        }
558 559 560
        if( !p_priv->thread_id )
            p_priv->thread_id = pthread_self();
        if( (i_error = pthread_setschedparam( p_priv->thread_id,
561
                                               i_policy, &param )) )
562
        {
563 564 565
            errno = i_error;
            msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
                      psz_file, i_line );
566
            i_priority = 0;
567 568
        }
    }
569 570 571 572

#elif defined( WIN32 ) || defined( UNDER_CE )
    VLC_UNUSED( psz_file); VLC_UNUSED( i_line );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
573 574 575
    if( !p_priv->thread_id )
        p_priv->thread_id = GetCurrentThread();
    if( !SetThreadPriority(p_priv->thread_id, i_priority) )
576 577 578 579 580
    {
        msg_Warn( p_this, "couldn't set a faster priority" );
        return 1;
    }

581
#endif
Gildas Bazin's avatar
 
Gildas Bazin committed
582 583

    return 0;
584 585
}

586 587 588 589 590
/*****************************************************************************
 * vlc_thread_ready: tell the parent thread we were successfully spawned
 *****************************************************************************/
void __vlc_thread_ready( vlc_object_t *p_this )
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
591
    vlc_object_signal( p_this );
592 593 594 595 596
}

/*****************************************************************************
 * vlc_thread_join: wait until a thread exits, inner version
 *****************************************************************************/
597
void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line )
598
{
599
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
600 601 602 603 604 605 606 607 608
    int i_ret = 0;

#if defined( LIBVLC_USE_PTHREAD )
    /* Make sure we do return if we are calling vlc_thread_join()
     * from the joined thread */
    if (pthread_equal (pthread_self (), p_priv->thread_id))
        i_ret = pthread_detach (p_priv->thread_id);
    else
        i_ret = pthread_join (p_priv->thread_id, NULL);
609

610
#elif defined( UNDER_CE ) || defined( WIN32 )
611 612 613 614 615
    HMODULE hmodule;
    BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*,
                                      FILETIME*, FILETIME* );
    FILETIME create_ft, exit_ft, kernel_ft, user_ft;
    int64_t real_time, kernel_time, user_time;
616
    HANDLE hThread;
617

618
    /*
619
    ** object will close its thread handle when destroyed, duplicate it here
620 621
    ** to be on the safe side
    */
622
    if( ! DuplicateHandle(GetCurrentProcess(),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
623
            p_priv->thread_id,
624 625 626 627 628 629
            GetCurrentProcess(),
            &hThread,
            0,
            FALSE,
            DUPLICATE_SAME_ACCESS) )
    {
630
        p_priv->b_thread = false;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
631 632
        i_ret = GetLastError();
        goto error;
633
    }
634

635
    WaitForSingleObject( hThread, INFINITE );
636 637 638 639 640 641 642 643 644 645 646

#if defined( UNDER_CE )
    hmodule = GetModuleHandle( _T("COREDLL") );
#else
    hmodule = GetModuleHandle( _T("KERNEL32") );
#endif
    OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*,
                                         FILETIME*, FILETIME* ))
        GetProcAddress( hmodule, _T("GetThreadTimes") );

    if( OurGetThreadTimes &&
647
        OurGetThreadTimes( hThread,
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
                           &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
    {
        real_time =
          ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
          ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
        real_time /= 10;

        kernel_time =
          ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
           kernel_ft.dwLowDateTime) / 10;

        user_time =
          ((((int64_t)user_ft.dwHighDateTime)<<32)|
           user_ft.dwLowDateTime) / 10;

        msg_Dbg( p_this, "thread times: "
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
664
                 "real %"PRId64"m%fs, kernel %"PRId64"m%fs, user %"PRId64"m%fs",
665 666 667 668 669 670 671
                 real_time/60/1000000,
                 (double)((real_time%(60*1000000))/1000000.0),
                 kernel_time/60/1000000,
                 (double)((kernel_time%(60*1000000))/1000000.0),
                 user_time/60/1000000,
                 (double)((user_time%(60*1000000))/1000000.0) );
    }
672
    CloseHandle( hThread );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
673
error:
674

675
#elif defined( HAVE_KERNEL_SCHEDULER_H )
676
    int32_t exit_value;
677
    i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value ));
678

679 680 681 682
#endif

    if( i_ret )
    {
683
        errno = i_ret;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
684 685
        msg_Err( p_this, "thread_join(%lu) failed at %s:%d (%m)",
                         (unsigned long)p_priv->thread_id, psz_file, i_line );
686 687
    }
    else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
688 689
        msg_Dbg( p_this, "thread %lu joined (%s:%d)",
                         (unsigned long)p_priv->thread_id, psz_file, i_line );
690

691
    p_priv->b_thread = false;
692
}