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

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
33
#include <vlc_atomic.h>
34 35 36 37

#include "libvlc.h"
#include <stdarg.h>
#include <assert.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38
#include <limits.h>
39
#include <errno.h>
Martell Malone's avatar
Martell Malone committed
40
#include <time.h>
41

42
/*** Static mutex and condition variable ***/
43 44
static vlc_mutex_t super_mutex;
static vlc_cond_t  super_variable;
45

46
#define IS_INTERRUPTIBLE (!VLC_WINSTORE_APP || _WIN32_WINNT >= 0x0A00)
47

Steve Lhomme's avatar
Steve Lhomme committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*** Threads ***/
static DWORD thread_key;

struct vlc_thread
{
    HANDLE         id;

    bool           killable;
#if IS_INTERRUPTIBLE
    bool           killed;
#else
    atomic_bool    killed;
#endif
    vlc_cleanup_t *cleaners;

    void        *(*entry) (void *);
    void          *data;
};

67
/*** Common helpers ***/
68
#if !IS_INTERRUPTIBLE
69 70 71
static bool isCancelled(void);
#endif

72 73 74
static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
                                         DWORD delay)
{
75 76 77
    DWORD ret;
    if (count == 0)
    {
78
#if !IS_INTERRUPTIBLE
79 80 81 82 83 84 85 86 87 88 89
        do {
            DWORD new_delay = 50;
            if (new_delay > delay)
                new_delay = delay;
            ret = SleepEx (new_delay, TRUE);
            if (delay != INFINITE)
                delay -= new_delay;
            if (isCancelled())
                ret = WAIT_IO_COMPLETION;
        } while (delay && ret == 0);
#else
90
        ret = SleepEx (delay, TRUE);
91 92
#endif

93 94 95
        if (ret == 0)
            ret = WAIT_TIMEOUT;
    }
96
    else {
97
#if !IS_INTERRUPTIBLE
98 99 100 101 102 103 104 105 106 107 108
        do {
            DWORD new_delay = 50;
            if (new_delay > delay)
                new_delay = delay;
            ret = WaitForMultipleObjectsEx (count, handles, FALSE, new_delay, TRUE);
            if (delay != INFINITE)
                delay -= new_delay;
            if (isCancelled())
                ret = WAIT_IO_COMPLETION;
        } while (delay && ret == WAIT_TIMEOUT);
#else
109
        ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
110 111
#endif
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
112

113 114
    /* We do not abandon objects... this would be a bug */
    assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
115

116 117 118
    if (unlikely(ret == WAIT_FAILED))
        abort (); /* We are screwed! */
    return ret;
119 120 121 122 123 124 125 126 127 128
}

static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
{
    return vlc_WaitForMultipleObjects (1, &handle, delay);
}

static DWORD vlc_Sleep (DWORD delay)
{
    DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
129
    return (ret != WAIT_TIMEOUT) ? ret : 0;
130 131 132
}


133
/*** Mutexes ***/
134
void vlc_mutex_init( vlc_mutex_t *p_mutex )
135 136 137 138
{
    /* This creates a recursive mutex. This is OK as fast mutexes have
     * no defined behavior in case of recursive locking. */
    InitializeCriticalSection (&p_mutex->mutex);
139
    p_mutex->dynamic = true;
140 141
}

142
void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
143 144
{
    InitializeCriticalSection( &p_mutex->mutex );
145
    p_mutex->dynamic = true;
146 147 148 149 150
}


void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
{
151
    assert (p_mutex->dynamic);
152 153 154 155 156
    DeleteCriticalSection (&p_mutex->mutex);
}

void vlc_mutex_lock (vlc_mutex_t *p_mutex)
{
157
    if (!p_mutex->dynamic)
158 159 160 161 162
    {   /* static mutexes */
        int canc = vlc_savecancel ();
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
163
        while (p_mutex->locked)
164 165
        {
            p_mutex->contention++;
166
            vlc_cond_wait (&super_variable, &super_mutex);
167 168
            p_mutex->contention--;
        }
169
        p_mutex->locked = true;
170 171
        vlc_mutex_unlock (&super_mutex);
        vlc_restorecancel (canc);
172
        return;
173
    }
174

175 176 177 178 179
    EnterCriticalSection (&p_mutex->mutex);
}

int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
{
180 181 182
    if (!p_mutex->dynamic)
    {   /* static mutexes */
        int ret = EBUSY;
183

184 185
        assert (p_mutex != &super_mutex); /* this one cannot be static */
        vlc_mutex_lock (&super_mutex);
186 187 188 189 190
        if (!p_mutex->locked)
        {
            p_mutex->locked = true;
            ret = 0;
        }
191
        vlc_mutex_unlock (&super_mutex);
192
        return ret;
193
    }
194

195 196 197 198 199
    return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
}

void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
{
200 201
    if (!p_mutex->dynamic)
    {   /* static mutexes */
202 203 204
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
205 206
        assert (p_mutex->locked);
        p_mutex->locked = false;
207 208 209
        if (p_mutex->contention)
            vlc_cond_broadcast (&super_variable);
        vlc_mutex_unlock (&super_mutex);
210 211 212
        return;
    }

213 214 215 216
    LeaveCriticalSection (&p_mutex->mutex);
}

/*** Condition variables ***/
217
void vlc_cond_init(vlc_cond_t *wait)
218
{
219 220
    wait->semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
    if (unlikely(wait->semaphore == NULL))
221
        abort();
222
    wait->waiters = 0;
223 224 225 226
}

void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
227
    vlc_cond_init (p_condvar);
228 229
}

230
void vlc_cond_destroy(vlc_cond_t *wait)
231
{
232
    CloseHandle(wait->semaphore);
233 234
}

235
static LONG InterlockedDecrementNonZero(LONG volatile *dst)
236
{
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    LONG cmp, val = 1;

    do
    {
        cmp = val;
        val = InterlockedCompareExchange(dst, val - 1, val);
        if (val == 0)
            return 0;
    }
    while (cmp != val);

    return val;
}

void vlc_cond_signal(vlc_cond_t *wait)
{
253
    if (wait->semaphore == NULL)
254 255 256 257
        return;

    if (InterlockedDecrementNonZero(&wait->waiters) > 0)
        ReleaseSemaphore(wait->semaphore, 1, NULL);
258 259
}

260
void vlc_cond_broadcast(vlc_cond_t *wait)
261
{
262
    if (wait->semaphore == NULL)
263 264
        return;

265 266 267
    LONG waiters = InterlockedExchange(&wait->waiters, 0);
    if (waiters > 0)
        ReleaseSemaphore(wait->semaphore, waiters, NULL);
268 269
}

270 271
static int vlc_cond_wait_delay(vlc_cond_t *wait, vlc_mutex_t *lock,
                               mtime_t us)
272
{
273 274 275 276 277 278
    if (us < 0)
        us = 0;
    if (us > 0x7fffffff)
        us = 0x7fffffff;

    DWORD delay = us;
279 280
    DWORD result;

281
    vlc_testcancel();
282

283
    if (wait->semaphore == NULL)
284
    {   /* FIXME FIXME FIXME */
285
        vlc_mutex_unlock(lock);
286
        result = SleepEx((delay > 50u) ? 50u : delay, TRUE);
287 288 289 290 291
    }
    else
    {
        InterlockedIncrement(&wait->waiters);
        vlc_mutex_unlock(lock);
292
        result = vlc_WaitForSingleObject(wait->semaphore, delay);
293
    }
294
    vlc_mutex_lock(lock);
295 296 297

    if (result == WAIT_IO_COMPLETION)
        vlc_testcancel();
298
    return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
299 300 301 302 303
}

void vlc_cond_wait(vlc_cond_t *wait, vlc_mutex_t *lock)
{
    vlc_cond_wait_delay(wait, lock, INFINITE);
304 305
}

306
int vlc_cond_timedwait(vlc_cond_t *wait, vlc_mutex_t *lock, mtime_t deadline)
307
{
308 309
    return vlc_cond_wait_delay(wait, lock, deadline - mdate());
}
310

311 312 313 314 315
int vlc_cond_timedwait_daytime(vlc_cond_t *wait, vlc_mutex_t *lock,
                               time_t deadline)
{
    time_t now;
    mtime_t delay;
316

317 318
    time(&now);
    delay = ((mtime_t)deadline - (mtime_t)now) * CLOCK_FREQ;
319

320
    return vlc_cond_wait_delay(wait, lock, delay);
321 322
}

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
/*** Semaphore ***/
void vlc_sem_init (vlc_sem_t *sem, unsigned value)
{
    *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
    if (*sem == NULL)
        abort ();
}

void vlc_sem_destroy (vlc_sem_t *sem)
{
    CloseHandle (*sem);
}

int vlc_sem_post (vlc_sem_t *sem)
{
    ReleaseSemaphore (*sem, 1, NULL);
    return 0; /* FIXME */
}

void vlc_sem_wait (vlc_sem_t *sem)
{
    DWORD result;

    do
    {
        vlc_testcancel ();
349
        result = vlc_WaitForSingleObject (*sem, INFINITE);
350 351 352 353
    }
    while (result == WAIT_IO_COMPLETION);
}

354
/*** Thread-specific variables (TLS) ***/
355
struct vlc_threadvar
356
{
357 358 359 360 361
    DWORD                 id;
    void                (*destroy) (void *);
    struct vlc_threadvar *prev;
    struct vlc_threadvar *next;
} *vlc_threadvar_last = NULL;
Pierre's avatar
Pierre committed
362

363 364 365 366 367 368 369 370
int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
{
    struct vlc_threadvar *var = malloc (sizeof (*var));
    if (unlikely(var == NULL))
        return errno;

    var->id = TlsAlloc();
    if (var->id == TLS_OUT_OF_INDEXES)
Rémi Duraffort's avatar
Rémi Duraffort committed
371 372
    {
        free (var);
373
        return EAGAIN;
Rémi Duraffort's avatar
Rémi Duraffort committed
374
    }
375 376 377 378
    var->destroy = destr;
    var->next = NULL;
    *p_tls = var;

379
    vlc_mutex_lock (&super_mutex);
380
    var->prev = vlc_threadvar_last;
381 382 383
    if (var->prev)
        var->prev->next = var;

384
    vlc_threadvar_last = var;
385
    vlc_mutex_unlock (&super_mutex);
386
    return 0;
387 388 389 390
}

void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
391 392
    struct vlc_threadvar *var = *p_tls;

393
    vlc_mutex_lock (&super_mutex);
394 395
    if (var->prev != NULL)
        var->prev->next = var->next;
396

397 398
    if (var->next != NULL)
        var->next->prev = var->prev;
399 400 401
    else
        vlc_threadvar_last = var->prev;

402
    vlc_mutex_unlock (&super_mutex);
403 404 405

    TlsFree (var->id);
    free (var);
406 407 408 409
}

int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
410 411 412 413 414 415
    int saved = GetLastError ();
    int val = TlsSetValue (key->id, value) ? ENOMEM : 0;

    if (val == 0)
        SetLastError(saved);
    return val;
416 417 418 419
}

void *vlc_threadvar_get (vlc_threadvar_t key)
{
420 421 422 423 424
    int saved = GetLastError ();
    void *value = TlsGetValue (key->id);

    SetLastError(saved);
    return value;
425 426
}

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
static void vlc_threadvars_cleanup(void)
{
    vlc_threadvar_t key;
retry:
    /* TODO: use RW lock or something similar */
    vlc_mutex_lock(&super_mutex);
    for (key = vlc_threadvar_last; key != NULL; key = key->prev)
    {
        void *value = vlc_threadvar_get(key);
        if (value != NULL && key->destroy != NULL)
        {
            vlc_mutex_unlock(&super_mutex);
            vlc_threadvar_set(key, NULL);
            key->destroy(value);
            goto retry;
        }
    }
    vlc_mutex_unlock(&super_mutex);
}

447
#if !IS_INTERRUPTIBLE
448 449
static bool isCancelled(void)
{
450
    struct vlc_thread *th = TlsGetValue(thread_key);
451 452 453 454 455 456 457
    if (th == NULL)
        return false; /* Main thread - cannot be cancelled anyway */

    return atomic_load(&th->killed);
}
#endif

458
static unsigned __stdcall vlc_entry (void *p)
459
{
460
    struct vlc_thread *th = p;
461

462
    TlsSetValue(thread_key, th);
463
    th->killable = true;
464
    th->data = th->entry (th->data);
465 466 467 468
    TlsSetValue(thread_key, NULL);

    if (th->id == NULL) /* Detached thread */
        free(th);
469 470 471
    return 0;
}

472 473
static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
                           void *(*entry) (void *), void *data, int priority)
474
{
475 476
    struct vlc_thread *th = malloc (sizeof (*th));
    if (unlikely(th == NULL))
477
        return ENOMEM;
478 479 480
    th->entry = entry;
    th->data = data;
    th->killable = false; /* not until vlc_entry() ! */
481
#if IS_INTERRUPTIBLE
482 483 484 485
    th->killed = false;
#else
    atomic_init(&th->killed, false);
#endif
486
    th->cleaners = NULL;
487

488 489 490 491
    /* When using the MSVCRT C library you have to use the _beginthreadex
     * function instead of CreateThread, otherwise you'll end up with
     * memory leaks and the signal functions not working (see Microsoft
     * Knowledge Base, article 104641) */
492
    uintptr_t h = _beginthreadex (NULL, 0, vlc_entry, th, 0, NULL);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
493
    if (h == 0)
494
    {
495 496 497
        int err = errno;
        free (th);
        return err;
498
    }
499

500
    if (detached)
501
    {
502
        CloseHandle((HANDLE)h);
503 504
        th->id = NULL;
    }
505 506
    else
        th->id = (HANDLE)h;
507

508 509
    if (p_handle != NULL)
        *p_handle = th;
510 511

    if (priority)
512
        SetThreadPriority (th->id, priority);
513

514
    return 0;
515 516
}

517 518 519
int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
                void *data, int priority)
{
Rémi Denis-Courmont's avatar
Typo  
Rémi Denis-Courmont committed
520
    return vlc_clone_attr (p_handle, false, entry, data, priority);
521 522
}

523
void vlc_join (vlc_thread_t th, void **result)
524 525 526
{
    do
        vlc_testcancel ();
527
    while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
528

529 530
    if (result != NULL)
        *result = th->data;
531
    CloseHandle (th->id);
532
    free (th);
533 534
}

535 536
int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
                      void *data, int priority)
537
{
538 539 540 541 542
    vlc_thread_t th;
    if (p_handle == NULL)
        p_handle = &th;

    return vlc_clone_attr (p_handle, true, entry, data, priority);
543
}
544

545 546 547 548 549 550 551
int vlc_set_priority (vlc_thread_t th, int priority)
{
    if (!SetThreadPriority (th->id, priority))
        return VLC_EGENERIC;
    return VLC_SUCCESS;
}

552 553
/*** Thread cancellation ***/

554
#if IS_INTERRUPTIBLE
555
/* APC procedure for thread cancellation */
556
static void CALLBACK vlc_cancel_self (ULONG_PTR self)
557
{
558 559 560 561
    struct vlc_thread *th = (void *)self;

    if (likely(th != NULL))
        th->killed = true;
562
}
563
#endif
564

565
void vlc_cancel (vlc_thread_t th)
566
{
567
#if IS_INTERRUPTIBLE
568 569 570
    QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
#else
    atomic_store (&th->killed, true);
571
#endif
572 573 574 575
}

int vlc_savecancel (void)
{
576
    struct vlc_thread *th = TlsGetValue(thread_key);
577
    if (th == NULL)
578 579
        return false; /* Main thread - cannot be cancelled anyway */

580 581
    int state = th->killable;
    th->killable = false;
582 583 584 585 586
    return state;
}

void vlc_restorecancel (int state)
{
587
    struct vlc_thread *th = TlsGetValue(thread_key);
588 589
    assert (state == false || state == true);

590
    if (th == NULL)
591 592
        return; /* Main thread - cannot be cancelled anyway */

593 594
    assert (!th->killable);
    th->killable = state != 0;
595 596 597 598
}

void vlc_testcancel (void)
{
599
    struct vlc_thread *th = TlsGetValue(thread_key);
600
    if (th == NULL)
601
        return; /* Main thread - cannot be cancelled anyway */
602 603
    if (!th->killable)
        return;
604
#if IS_INTERRUPTIBLE
605 606 607 608 609 610
    if (likely(!th->killed))
        return;
#else
    if (!atomic_load(&th->killed))
        return;
#endif
611

612 613
    for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
        p->proc (p->data);
614

615
    th->data = NULL; /* TODO: special value? */
616 617 618
    TlsSetValue(thread_key, NULL);
    if (th->id == NULL) /* Detached thread */
        free(th);
619
    _endthreadex(0);
620 621 622 623 624 625 626 627
}

void vlc_control_cancel (int cmd, ...)
{
    /* NOTE: This function only modifies thread-specific data, so there is no
     * need to lock anything. */
    va_list ap;

628
    struct vlc_thread *th = TlsGetValue(thread_key);
629
    if (th == NULL)
630 631 632 633 634 635 636 637 638 639
        return; /* Main thread - cannot be cancelled anyway */

    va_start (ap, cmd);
    switch (cmd)
    {
        case VLC_CLEANUP_PUSH:
        {
            /* cleaner is a pointer to the caller stack, no need to allocate
             * and copy anything. As a nice side effect, this cannot fail. */
            vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
640 641
            cleaner->next = th->cleaners;
            th->cleaners = cleaner;
642 643 644 645 646
            break;
        }

        case VLC_CLEANUP_POP:
        {
647
            th->cleaners = th->cleaners->next;
648 649 650 651 652
            break;
        }
    }
    va_end (ap);
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
653

654
/*** Clock ***/
655 656
static CRITICAL_SECTION clock_lock;

657 658 659 660 661 662 663
static mtime_t mdate_giveup (void)
{
    abort ();
}

static mtime_t (*mdate_selected) (void) = mdate_giveup;

664 665
mtime_t mdate (void)
{
666 667 668
    return mdate_selected ();
}

669 670
static union
{
671
#if (_WIN32_WINNT < 0x0601)
672 673
    struct
    {
674 675
        BOOL (*query) (PULONGLONG);
    } interrupt;
676 677
#endif
#if (_WIN32_WINNT < 0x0600)
678 679 680 681
    struct
    {
        ULONGLONG (*get) (void);
    } tick;
682
#endif
683 684 685 686
    struct
    {
        LARGE_INTEGER freq;
    } perf;
687 688
} clk;

689 690
static mtime_t mdate_interrupt (void)
{
691
    ULONGLONG ts;
692
    BOOL ret;
693

694 695 696 697 698 699
#if (_WIN32_WINNT >= 0x0601)
    ret = QueryUnbiasedInterruptTime (&ts);
#else
    ret = clk.interrupt.query (&ts);
#endif
    if (unlikely(!ret))
700 701
        abort ();

702 703 704
    /* hundreds of nanoseconds */
    static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
    return ts / (10000000 / CLOCK_FREQ);
705
}
706

707 708
static mtime_t mdate_tick (void)
{
709 710 711 712 713
#if (_WIN32_WINNT >= 0x0600)
    ULONGLONG ts = GetTickCount64 ();
#else
    ULONGLONG ts = clk.tick.get ();
#endif
714 715 716 717

    /* milliseconds */
    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
    return ts * (CLOCK_FREQ / 1000);
718
}
719
#if !VLC_WINSTORE_APP
720 721 722 723
#include <mmsystem.h>
static mtime_t mdate_multimedia (void)
{
     DWORD ts = timeGetTime ();
724

725 726 727 728
    /* milliseconds */
    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
    return ts * (CLOCK_FREQ / 1000);
}
729
#endif
730 731 732

static mtime_t mdate_perf (void)
{
733
    /* We don't need the real date, just the value of a high precision timer */
734 735 736
    LARGE_INTEGER counter;
    if (!QueryPerformanceCounter (&counter))
        abort ();
737 738 739

    /* Convert to from (1/freq) to microsecond resolution */
    /* We need to split the division to avoid 63-bits overflow */
740
    lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
741

742
    return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
743
}
744

745 746 747 748 749
static mtime_t mdate_wall (void)
{
    FILETIME ts;
    ULARGE_INTEGER s;

750
#if (_WIN32_WINNT >= 0x0602) && !VLC_WINSTORE_APP
751 752 753
    GetSystemTimePreciseAsFileTime (&ts);
#else
    GetSystemTimeAsFileTime (&ts);
754
#endif
755 756 757 758 759
    s.LowPart = ts.dwLowDateTime;
    s.HighPart = ts.dwHighDateTime;
    /* hundreds of nanoseconds */
    static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
    return s.QuadPart / (10000000 / CLOCK_FREQ);
760 761 762 763 764 765 766 767 768 769 770 771 772
}

#undef mwait
void mwait (mtime_t deadline)
{
    mtime_t delay;

    vlc_testcancel();
    while ((delay = (deadline - mdate())) > 0)
    {
        delay /= 1000;
        if (unlikely(delay > 0x7fffffff))
            delay = 0x7fffffff;
773
        vlc_Sleep (delay);
774 775 776 777 778 779 780 781 782 783
        vlc_testcancel();
    }
}

#undef msleep
void msleep (mtime_t delay)
{
    mwait (mdate () + delay);
}

784
static void SelectClockSource (vlc_object_t *obj)
785 786 787 788 789 790 791 792
{
    EnterCriticalSection (&clock_lock);
    if (mdate_selected != mdate_giveup)
    {
        LeaveCriticalSection (&clock_lock);
        return;
    }

Rafaël Carré's avatar
Rafaël Carré committed
793
#if VLC_WINSTORE_APP
794 795
    const char *name = "perf";
#else
796
    const char *name = "multimedia";
797
#endif
798 799 800 801 802 803
    char *str = var_InheritString (obj, "clock-source");
    if (str != NULL)
        name = str;
    if (!strcmp (name, "interrupt"))
    {
        msg_Dbg (obj, "using interrupt time as clock source");
804 805 806 807 808
#if (_WIN32_WINNT < 0x0601)
        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
        if (unlikely(h == NULL))
            abort ();
        clk.interrupt.query = (void *)GetProcAddress (h,
809
                                                      "QueryUnbiasedInterruptTime");
810 811 812
        if (unlikely(clk.interrupt.query == NULL))
            abort ();
#endif
813 814 815 816 817 818
        mdate_selected = mdate_interrupt;
    }
    else
    if (!strcmp (name, "tick"))
    {
        msg_Dbg (obj, "using Windows time as clock source");
819
#if (_WIN32_WINNT < 0x0600)
820 821 822
        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
        if (unlikely(h == NULL))
            abort ();
823
        clk.tick.get = (void *)GetProcAddress (h, "GetTickCount64");
824 825 826
        if (unlikely(clk.tick.get == NULL))
            abort ();
#endif
827 828
        mdate_selected = mdate_tick;
    }
829
#if !VLC_WINSTORE_APP
830 831 832 833 834 835 836 837 838 839 840 841
    else
    if (!strcmp (name, "multimedia"))
    {
        TIMECAPS caps;

        msg_Dbg (obj, "using multimedia timers as clock source");
        if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
            abort ();
        msg_Dbg (obj, " min period: %u ms, max period: %u ms",
                 caps.wPeriodMin, caps.wPeriodMax);
        mdate_selected = mdate_multimedia;
    }
842
#endif
843 844 845 846
    else
    if (!strcmp (name, "perf"))
    {
        msg_Dbg (obj, "using performance counters as clock source");
847
        if (!QueryPerformanceFrequency (&clk.perf.freq))
848
            abort ();
849
        msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        mdate_selected = mdate_perf;
    }
    else
    if (!strcmp (name, "wall"))
    {
        msg_Dbg (obj, "using system time as clock source");
        mdate_selected = mdate_wall;
    }
    else
    {
        msg_Err (obj, "invalid clock source \"%s\"", name);
        abort ();
    }
    LeaveCriticalSection (&clock_lock);
    free (str);
}

867 868
size_t EnumClockSource (vlc_object_t *obj, const char *var,
                        char ***vp, char ***np)
869 870 871 872 873 874
{
    const size_t max = 6;
    char **values = xmalloc (sizeof (*values) * max);
    char **names = xmalloc (sizeof (*names) * max);
    size_t n = 0;

875
#if (_WIN32_WINNT < 0x0601)
876 877 878 879
    DWORD version = LOWORD(GetVersion());
    version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
#endif

880 881 882
    values[n] = xstrdup ("");
    names[n] = xstrdup (_("Auto"));
    n++;
883 884
#if (_WIN32_WINNT < 0x0601)
    if (version >= 0x0601)
885
#endif
886 887 888 889 890
    {
        values[n] = xstrdup ("interrupt");
        names[n] = xstrdup ("Interrupt time");
        n++;
    }
891 892
#if (_WIN32_WINNT < 0x0600)
    if (version >= 0x0600)
893
#endif
894 895 896 897 898
    {
        values[n] = xstrdup ("tick");
        names[n] = xstrdup ("Windows time");
        n++;
    }
899
#if !VLC_WINSTORE_APP
900 901 902
    values[n] = xstrdup ("multimedia");
    names[n] = xstrdup ("Multimedia timers");
    n++;
903
#endif
904 905 906 907 908 909 910 911 912
    values[n] = xstrdup ("perf");
    names[n] = xstrdup ("Performance counters");
    n++;
    values[n] = xstrdup ("wall");
    names[n] = xstrdup ("System time (DANGEROUS!)");
    n++;

    *vp = values;
    *np = names;
913
    (void) obj; (void) var;
914 915 916
    return n;
}

917

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
918
/*** Timers ***/
919 920 921 922 923 924 925
struct vlc_timer
{
    HANDLE handle;
    void (*func) (void *);
    void *data;
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
926 927
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
928
    struct vlc_timer *timer = val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
929 930

    assert (timeout);
931
    timer->func (timer->data);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
932 933
}

934
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
935
{
936 937 938 939 940 941 942 943
    struct vlc_timer *timer = malloc (sizeof (*timer));

    if (timer == NULL)
        return ENOMEM;
    timer->func = func;
    timer->data = data;
    timer->handle = INVALID_HANDLE_VALUE;
    *id = timer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
944 945 946
    return 0;
}

947
void vlc_timer_destroy (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
948
{
949
#if !VLC_WINSTORE_APP
950 951
    if (timer->handle != INVALID_HANDLE_VALUE)
        DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
952
#endif
953
    free (timer);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
954 955
}

956
void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
957 958
                         mtime_t value, mtime_t interval)
{
959
    if (timer->handle != INVALID_HANDLE_VALUE)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
960
    {
961
#if !VLC_WINSTORE_APP
962
        DeleteTimerQueueTimer (NULL, timer->handle, NULL);
963
#endif
964
        timer->handle = INVALID_HANDLE_VALUE;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
965 966 967 968 969 970 971 972
    }
    if (value == 0)
        return; /* Disarm */

    if (absolute)
        value -= mdate ();
    value = (value + 999) / 1000;
    interval = (interval + 999) / 1000;
Pierre Ynard's avatar
Pierre Ynard committed
973

974
#if !VLC_WINSTORE_APP
975 976
    if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
                                value, interval, WT_EXECUTEDEFAULT))
977
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
978 979 980
        abort ();
}

981
unsigned vlc_timer_getoverrun (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
982
{
983
    (void)timer;
984
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
985
}
986 987 988 989 990


/*** CPU ***/
unsigned vlc_GetCPUCount (void)
{