thread.c 23 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
33
34
35
36
 *****************************************************************************/

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

#include <vlc_common.h>

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

40
/*** Static mutex and condition variable ***/
41
42
static vlc_mutex_t super_mutex;
static vlc_cond_t  super_variable;
43

44

45
/*** Common helpers ***/
46
47
48
static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
                                         DWORD delay)
{
49
50
51
52
53
54
55
56
57
    DWORD ret;
    if (count == 0)
    {
        ret = SleepEx (delay, TRUE);
        if (ret == 0)
            ret = WAIT_TIMEOUT;
    }
    else
        ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
58

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

62
63
64
    if (unlikely(ret == WAIT_FAILED))
        abort (); /* We are screwed! */
    return ret;
65
66
67
68
69
70
71
72
73
74
}

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);
75
    return (ret != WAIT_TIMEOUT) ? ret : 0;
76
77
78
}


79
/*** Mutexes ***/
80
void vlc_mutex_init( vlc_mutex_t *p_mutex )
81
82
83
84
{
    /* This creates a recursive mutex. This is OK as fast mutexes have
     * no defined behavior in case of recursive locking. */
    InitializeCriticalSection (&p_mutex->mutex);
85
    p_mutex->dynamic = true;
86
87
}

88
void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
89
90
{
    InitializeCriticalSection( &p_mutex->mutex );
91
    p_mutex->dynamic = true;
92
93
94
95
96
}


void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
{
97
    assert (p_mutex->dynamic);
98
99
100
101
102
    DeleteCriticalSection (&p_mutex->mutex);
}

void vlc_mutex_lock (vlc_mutex_t *p_mutex)
{
103
    if (!p_mutex->dynamic)
104
105
106
107
108
    {   /* static mutexes */
        int canc = vlc_savecancel ();
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
109
        while (p_mutex->locked)
110
111
        {
            p_mutex->contention++;
112
            vlc_cond_wait (&super_variable, &super_mutex);
113
114
            p_mutex->contention--;
        }
115
        p_mutex->locked = true;
116
117
        vlc_mutex_unlock (&super_mutex);
        vlc_restorecancel (canc);
118
        return;
119
    }
120

121
122
123
124
125
    EnterCriticalSection (&p_mutex->mutex);
}

int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
{
126
127
128
    if (!p_mutex->dynamic)
    {   /* static mutexes */
        int ret = EBUSY;
129

130
131
        assert (p_mutex != &super_mutex); /* this one cannot be static */
        vlc_mutex_lock (&super_mutex);
132
133
134
135
136
        if (!p_mutex->locked)
        {
            p_mutex->locked = true;
            ret = 0;
        }
137
        vlc_mutex_unlock (&super_mutex);
138
        return ret;
139
    }
140

141
142
143
144
145
    return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
}

void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
{
146
147
    if (!p_mutex->dynamic)
    {   /* static mutexes */
148
149
150
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
151
152
        assert (p_mutex->locked);
        p_mutex->locked = false;
153
154
155
        if (p_mutex->contention)
            vlc_cond_broadcast (&super_variable);
        vlc_mutex_unlock (&super_mutex);
156
157
158
        return;
    }

159
160
161
162
    LeaveCriticalSection (&p_mutex->mutex);
}

/*** Condition variables ***/
163
164
enum
{
165
    CLOCK_STATIC=0, /* must be zero for VLC_STATIC_COND */
166
    CLOCK_MONOTONIC,
167
    CLOCK_REALTIME,
168
169
170
};

static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
171
172
{
    /* Create a manual-reset event (manual reset is needed for broadcast). */
173
174
    p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (!p_condvar->handle)
175
        abort();
176
177
178
179
180
181
182
183
184
185
186
    p_condvar->clock = clock;
}

void vlc_cond_init (vlc_cond_t *p_condvar)
{
    vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
}

void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
{
    vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
187
188
189
190
}

void vlc_cond_destroy (vlc_cond_t *p_condvar)
{
191
    CloseHandle (p_condvar->handle);
192
193
194
195
}

void vlc_cond_signal (vlc_cond_t *p_condvar)
{
196
    if (!p_condvar->clock)
197
198
199
200
        return;

    /* This is suboptimal but works. */
    vlc_cond_broadcast (p_condvar);
201
202
203
204
}

void vlc_cond_broadcast (vlc_cond_t *p_condvar)
{
205
    if (!p_condvar->clock)
206
207
208
        return;

    /* Wake all threads up (as the event HANDLE has manual reset) */
209
    SetEvent (p_condvar->handle);
210
211
212
213
214
215
}

void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
{
    DWORD result;

216
    if (!p_condvar->clock)
217
218
219
220
221
    {   /* FIXME FIXME FIXME */
        msleep (50000);
        return;
    }

222
223
224
    do
    {
        vlc_testcancel ();
225
        vlc_mutex_unlock (p_mutex);
226
        result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
227
        vlc_mutex_lock (p_mutex);
228
229
230
    }
    while (result == WAIT_IO_COMPLETION);

231
    ResetEvent (p_condvar->handle);
232
233
234
235
236
237
238
239
240
241
242
}

int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
                        mtime_t deadline)
{
    DWORD result;

    do
    {
        vlc_testcancel ();

243
244
245
        mtime_t total;
        switch (p_condvar->clock)
        {
246
247
248
            case CLOCK_MONOTONIC:
                total = mdate();
                break;
249
250
251
252
            case CLOCK_REALTIME: /* FIXME? sub-second precision */
                total = CLOCK_FREQ * time (NULL);
                break;
            default:
253
254
255
256
                assert (!p_condvar->clock);
                /* FIXME FIXME FIXME */
                msleep (50000);
                return 0;
257
258
        }
        total = (deadline - total) / 1000;
259
260
261
262
        if( total < 0 )
            total = 0;

        DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
263
        vlc_mutex_unlock (p_mutex);
264
        result = vlc_WaitForSingleObject (p_condvar->handle, delay);
265
        vlc_mutex_lock (p_mutex);
266
267
268
    }
    while (result == WAIT_IO_COMPLETION);

269
    ResetEvent (p_condvar->handle);
270
271
272
273

    return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
}

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*** 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 ();
300
        result = vlc_WaitForSingleObject (*sem, INFINITE);
301
302
303
304
    }
    while (result == WAIT_IO_COMPLETION);
}

305
/*** Thread-specific variables (TLS) ***/
306
struct vlc_threadvar
307
{
308
309
310
311
312
    DWORD                 id;
    void                (*destroy) (void *);
    struct vlc_threadvar *prev;
    struct vlc_threadvar *next;
} *vlc_threadvar_last = NULL;
Pierre's avatar
Pierre committed
313

314
315
316
317
318
319
320
321
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)
ivoire's avatar
ivoire committed
322
323
    {
        free (var);
324
        return EAGAIN;
ivoire's avatar
ivoire committed
325
    }
326
327
328
329
    var->destroy = destr;
    var->next = NULL;
    *p_tls = var;

330
    vlc_mutex_lock (&super_mutex);
331
    var->prev = vlc_threadvar_last;
sebastien's avatar
sebastien committed
332
333
334
    if (var->prev)
        var->prev->next = var;

335
    vlc_threadvar_last = var;
336
    vlc_mutex_unlock (&super_mutex);
337
    return 0;
338
339
340
341
}

void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
342
343
    struct vlc_threadvar *var = *p_tls;

344
    vlc_mutex_lock (&super_mutex);
345
346
    if (var->prev != NULL)
        var->prev->next = var->next;
sebastien's avatar
sebastien committed
347

348
349
    if (var->next != NULL)
        var->next->prev = var->prev;
sebastien's avatar
sebastien committed
350
351
352
    else
        vlc_threadvar_last = var->prev;

353
    vlc_mutex_unlock (&super_mutex);
354
355
356

    TlsFree (var->id);
    free (var);
357
358
359
360
}

int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
361
    return TlsSetValue (key->id, value) ? ENOMEM : 0;
362
363
364
365
}

void *vlc_threadvar_get (vlc_threadvar_t key)
{
366
367
368
    return TlsGetValue (key->id);
}

369
/*** Threads ***/
370
371
372
373
static vlc_threadvar_t thread_key;

/** Per-thread data */
struct vlc_thread
374
{
375
376
377
378
379
380
381
382
383
384
    HANDLE         id;

    bool           detached;
    bool           killable;
    bool           killed;
    vlc_cleanup_t *cleaners;

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

386
static void vlc_thread_cleanup (struct vlc_thread *th)
387
388
389
{
    vlc_threadvar_t key;

390
retry:
391
    /* TODO: use RW lock or something similar */
392
    vlc_mutex_lock (&super_mutex);
393
394
395
    for (key = vlc_threadvar_last; key != NULL; key = key->prev)
    {
        void *value = vlc_threadvar_get (key);
396
        if (value != NULL && key->destroy != NULL)
397
        {
398
            vlc_mutex_unlock (&super_mutex);
399
            vlc_threadvar_set (key, NULL);
400
            key->destroy (value);
401
402
            goto retry;
        }
403
    }
404
    vlc_mutex_unlock (&super_mutex);
405

406
407
408
409
410
    if (th->detached)
    {
        CloseHandle (th->id);
        free (th);
    }
411
412
}

413
static unsigned __stdcall vlc_entry (void *p)
414
{
415
    struct vlc_thread *th = p;
416

417
418
    vlc_threadvar_set (thread_key, th);
    th->killable = true;
419
    th->data = th->entry (th->data);
420
    vlc_thread_cleanup (th);
421
422
423
    return 0;
}

424
425
static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
                           void *(*entry) (void *), void *data, int priority)
426
{
427
428
    struct vlc_thread *th = malloc (sizeof (*th));
    if (unlikely(th == NULL))
429
        return ENOMEM;
430
431
    th->entry = entry;
    th->data = data;
432
    th->detached = detached;
433
434
435
    th->killable = false; /* not until vlc_entry() ! */
    th->killed = false;
    th->cleaners = NULL;
436

437
    HANDLE hThread;
438
439
440
441
    /* 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) */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
442
443
444
445
    uintptr_t h;

    h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
    if (h == 0)
446
    {
447
448
449
        int err = errno;
        free (th);
        return err;
450
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
451
    hThread = (HANDLE)h;
452

453
    /* Thread is suspended, so we can safely set th->id */
454
    th->id = hThread;
455
456
    if (p_handle != NULL)
        *p_handle = th;
457
458
459
460

    if (priority)
        SetThreadPriority (hThread, priority);

461
462
    ResumeThread (hThread);

463
    return 0;
464
465
}

466
467
468
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
469
    return vlc_clone_attr (p_handle, false, entry, data, priority);
470
471
}

472
void vlc_join (vlc_thread_t th, void **result)
473
474
475
{
    do
        vlc_testcancel ();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
476
    while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
477

478
479
    if (result != NULL)
        *result = th->data;
480
    CloseHandle (th->id);
481
    free (th);
482
483
}

484
485
int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
                      void *data, int priority)
486
{
487
488
489
490
491
    vlc_thread_t th;
    if (p_handle == NULL)
        p_handle = &th;

    return vlc_clone_attr (p_handle, true, entry, data, priority);
492
}
493

494
495
496
497
498
499
500
int vlc_set_priority (vlc_thread_t th, int priority)
{
    if (!SetThreadPriority (th->id, priority))
        return VLC_EGENERIC;
    return VLC_SUCCESS;
}

501
502
503
/*** Thread cancellation ***/

/* APC procedure for thread cancellation */
504
static void CALLBACK vlc_cancel_self (ULONG_PTR self)
505
{
506
    struct vlc_thread *th = (void *)self;
507

508
509
    if (likely(th != NULL))
        th->killed = true;
510
511
}

512
void vlc_cancel (vlc_thread_t th)
513
{
514
    QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
515
516
517
518
}

int vlc_savecancel (void)
{
519
520
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
521
522
        return false; /* Main thread - cannot be cancelled anyway */

523
524
    int state = th->killable;
    th->killable = false;
525
526
527
528
529
    return state;
}

void vlc_restorecancel (int state)
{
530
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
531
532
    assert (state == false || state == true);

533
    if (th == NULL)
534
535
        return; /* Main thread - cannot be cancelled anyway */

536
537
    assert (!th->killable);
    th->killable = state != 0;
538
539
540
541
}

void vlc_testcancel (void)
{
542
543
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
544
545
        return; /* Main thread - cannot be cancelled anyway */

546
    if (th->killable && th->killed)
547
    {
548
        for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
549
             p->proc (p->data);
550
551
552

        th->data = NULL; /* TODO: special value? */
        vlc_thread_cleanup (th);
553
        _endthreadex(0);
554
555
556
557
558
559
560
561
562
    }
}

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;

563
564
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
565
566
567
568
569
570
571
572
573
574
        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 *);
575
576
            cleaner->next = th->cleaners;
            th->cleaners = cleaner;
577
578
579
580
581
            break;
        }

        case VLC_CLEANUP_POP:
        {
582
            th->cleaners = th->cleaners->next;
583
584
585
586
587
            break;
        }
    }
    va_end (ap);
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
588

589
/*** Clock ***/
590
591
static CRITICAL_SECTION clock_lock;

592
593
594
595
596
597
598
static mtime_t mdate_giveup (void)
{
    abort ();
}

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

599
600
mtime_t mdate (void)
{
601
602
603
    return mdate_selected ();
}

604
605
606
607
static union
{
    struct
    {
608
609
610
611
#if (_WIN32_WINNT < 0x0601)
        BOOL (*query) (PULONGLONG);
#endif
    } interrupt;
612
613
614
615
616
617
    struct
    {
#if (_WIN32_WINNT < 0x0600)
        ULONGLONG (*get) (void);
#endif
    } tick;
618
619
620
621
    struct
    {
        LARGE_INTEGER freq;
    } perf;
622
623
} clk;

624
625
static mtime_t mdate_interrupt (void)
{
626
    ULONGLONG ts;
627
    BOOL ret;
628

629
630
631
632
633
634
#if (_WIN32_WINNT >= 0x0601)
    ret = QueryUnbiasedInterruptTime (&ts);
#else
    ret = clk.interrupt.query (&ts);
#endif
    if (unlikely(!ret))
635
636
        abort ();

637
638
639
    /* hundreds of nanoseconds */
    static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
    return ts / (10000000 / CLOCK_FREQ);
640
}
641

642
643
static mtime_t mdate_tick (void)
{
644
645
646
647
648
#if (_WIN32_WINNT >= 0x0600)
    ULONGLONG ts = GetTickCount64 ();
#else
    ULONGLONG ts = clk.tick.get ();
#endif
649
650
651
652

    /* milliseconds */
    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
    return ts * (CLOCK_FREQ / 1000);
653
654
655
656
657
}
#include <mmsystem.h>
static mtime_t mdate_multimedia (void)
{
     DWORD ts = timeGetTime ();
658

659
660
661
662
663
664
665
    /* milliseconds */
    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
    return ts * (CLOCK_FREQ / 1000);
}

static mtime_t mdate_perf (void)
{
666
    /* We don't need the real date, just the value of a high precision timer */
667
668
669
    LARGE_INTEGER counter;
    if (!QueryPerformanceCounter (&counter))
        abort ();
670
671
672

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

675
    return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
676
}
677

678
679
680
681
682
683
684
685
686
static mtime_t mdate_wall (void)
{
    FILETIME ts;
    ULARGE_INTEGER s;

#if (_WIN32_WINNT >= 0x0602)
    GetSystemTimePreciseAsFileTime (&ts);
#else
    GetSystemTimeAsFileTime (&ts);
687
#endif
688
689
690
691
692
    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);
693
694
695
696
697
698
699
700
701
702
703
704
705
}

#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;
706
        vlc_Sleep (delay);
707
708
709
710
711
712
713
714
715
716
        vlc_testcancel();
    }
}

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

717
static void SelectClockSource (vlc_object_t *obj)
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
{
    EnterCriticalSection (&clock_lock);
    if (mdate_selected != mdate_giveup)
    {
        LeaveCriticalSection (&clock_lock);
        return;
    }

    const char *name = "perf";
    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");
733
734
735
736
737
738
739
740
741
#if (_WIN32_WINNT < 0x0601)
        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
        if (unlikely(h == NULL))
            abort ();
        clk.interrupt.query = (void *)GetProcAddress (h,
                                                      _T("QueryUnbiasedInterruptTime"));
        if (unlikely(clk.interrupt.query == NULL))
            abort ();
#endif
742
743
744
745
746
747
        mdate_selected = mdate_interrupt;
    }
    else
    if (!strcmp (name, "tick"))
    {
        msg_Dbg (obj, "using Windows time as clock source");
748
749
750
751
752
753
754
755
#if (_WIN32_WINNT < 0x0601)
        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
        if (unlikely(h == NULL))
            abort ();
        clk.tick.get = (void *)GetProcAddress (h, _T("GetTickCount64"));
        if (unlikely(clk.tick.get == NULL))
            abort ();
#endif
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
        mdate_selected = mdate_tick;
    }
    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;
    }
    else
    if (!strcmp (name, "perf"))
    {
        msg_Dbg (obj, "using performance counters as clock source");
774
        if (!QueryPerformanceFrequency (&clk.perf.freq))
775
            abort ();
776
        msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
        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);
}

#define xstrdup(str) (strdup(str) ?: (abort(), NULL))

size_t EnumClockSource (vlc_object_t *obj, char ***vp, char ***np)
{
    const size_t max = 6;
    char **values = xmalloc (sizeof (*values) * max);
    char **names = xmalloc (sizeof (*names) * max);
    size_t n = 0;

803
#if (_WIN32_WINNT < 0x0601)
804
805
806
807
    DWORD version = LOWORD(GetVersion());
    version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
#endif

808
809
810
    values[n] = xstrdup ("");
    names[n] = xstrdup (_("Auto"));
    n++;
811
812
#if (_WIN32_WINNT < 0x0601)
    if (version >= 0x0601)
813
#endif
814
815
816
817
818
    {
        values[n] = xstrdup ("interrupt");
        names[n] = xstrdup ("Interrupt time");
        n++;
    }
819
820
#if (_WIN32_WINNT < 0x0600)
    if (version >= 0x0600)
821
#endif
822
823
824
825
826
    {
        values[n] = xstrdup ("tick");
        names[n] = xstrdup ("Windows time");
        n++;
    }
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    values[n] = xstrdup ("multimedia");
    names[n] = xstrdup ("Multimedia timers");
    n++;
    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;
    (void) obj;
    return n;
}

843

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
844
/*** Timers ***/
845
846
847
848
849
850
851
struct vlc_timer
{
    HANDLE handle;
    void (*func) (void *);
    void *data;
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
852
853
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
854
    struct vlc_timer *timer = val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
855
856

    assert (timeout);
857
    timer->func (timer->data);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
858
859
}

860
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
861
{
862
863
864
865
866
867
868
869
    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
870
871
872
    return 0;
}

873
void vlc_timer_destroy (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
874
{
875
876
877
    if (timer->handle != INVALID_HANDLE_VALUE)
        DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
    free (timer);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
878
879
}

880
void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
881
882
                         mtime_t value, mtime_t interval)
{
883
    if (timer->handle != INVALID_HANDLE_VALUE)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
884
    {
885
886
        DeleteTimerQueueTimer (NULL, timer->handle, NULL);
        timer->handle = INVALID_HANDLE_VALUE;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
887
888
889
890
891
892
893
894
    }
    if (value == 0)
        return; /* Disarm */

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

896
897
    if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
                                value, interval, WT_EXECUTEDEFAULT))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
898
899
900
        abort ();
}

901
unsigned vlc_timer_getoverrun (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
902
{
903
    (void)timer;
904
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
905
}
906
907
908
909
910


/*** CPU ***/
unsigned vlc_GetCPUCount (void)
{
911
912
    DWORD_PTR process;
    DWORD_PTR system;
913
914

    if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
915
        return popcount (system);
916
917
     return 1;
}
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956


/*** Initialization ***/
void vlc_threads_setup (libvlc_int_t *p_libvlc)
{
    SelectClockSource (VLC_OBJECT(p_libvlc));
}

extern vlc_rwlock_t config_lock, msg_lock;
BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);

BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
{
    (void) hinstDll;
    (void) lpvReserved;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            InitializeCriticalSection (&clock_lock);
            vlc_mutex_init (&super_mutex);
            vlc_cond_init (&super_variable);
            vlc_threadvar_create (&thread_key, NULL);
            vlc_rwlock_init (&config_lock);
            vlc_rwlock_init (&msg_lock);
            vlc_CPU_init ();
            break;

        case DLL_PROCESS_DETACH:
            vlc_rwlock_destroy (&msg_lock);
            vlc_rwlock_destroy (&config_lock);
            vlc_threadvar_delete (&thread_key);
            vlc_cond_destroy (&super_variable);
            vlc_mutex_destroy (&super_mutex);
            DeleteCriticalSection (&clock_lock);
            break;
    }
    return TRUE;
}