thread.c 23.9 KB
Newer Older
1
/*****************************************************************************
2
 * thread.c : Win32 back-end for LibVLC
3
4
5
6
7
8
9
10
 *****************************************************************************
 * Copyright (C) 1999-2009 the VideoLAN team
 *
 * 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
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 *
 * 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.
 *
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#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>
Pierre Ynard's avatar
Pierre Ynard committed
39
40
41
#ifdef UNDER_CE
# include <mmsystem.h>
#endif
42

43
static vlc_threadvar_t thread_key;
44
45

/**
46
 * Per-thread data
47
 */
48
struct vlc_thread
49
{
50
    HANDLE         id;
51
52
53
#ifdef UNDER_CE
    HANDLE         cancel_event;
#endif
54

55
    bool           detached;
56
57
    bool           killable;
    bool           killed;
58
    vlc_cleanup_t *cleaners;
59

60
61
62
    void        *(*entry) (void *);
    void          *data;
};
63
64
65
66
67
68
69

#ifdef UNDER_CE
static void CALLBACK vlc_cancel_self (ULONG_PTR dummy);

static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles,
                                  DWORD delay)
{
70
71
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
72
73
74
75
76
77
    {
        /* Main thread - cannot be cancelled anyway */
        return WaitForMultipleObjects (count, handles, FALSE, delay);
    }
    HANDLE new_handles[count + 1];
    memcpy(new_handles, handles, count * sizeof(HANDLE));
78
    new_handles[count] = th->cancel_event;
79
80
81
82
    DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE,
                                           delay);
    if (result == WAIT_OBJECT_0 + count)
    {
83
        vlc_cancel_self ((uintptr_t)th);
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        return WAIT_IO_COMPLETION;
    }
    else
    {
        return result;
    }
}

DWORD SleepEx (DWORD dwMilliseconds, BOOL bAlertable)
{
    if (bAlertable)
    {
        DWORD result = vlc_cancelable_wait (0, NULL, dwMilliseconds);
        return (result == WAIT_TIMEOUT) ? 0 : WAIT_IO_COMPLETION;
    }
    else
    {
        Sleep(dwMilliseconds);
        return 0;
    }
}

DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds,
                             BOOL bAlertable)
{
    if (bAlertable)
    {
        /* The MSDN documentation specifies different return codes,
         * but in practice they are the same. We just check that it
         * remains so. */
#if WAIT_ABANDONED != WAIT_ABANDONED_0
# error Windows headers changed, code needs to be rewritten!
#endif
        return vlc_cancelable_wait (1, &hHandle, dwMilliseconds);
    }
    else
    {
        return WaitForSingleObject (hHandle, dwMilliseconds);
    }
}

DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles,
                                BOOL bWaitAll, DWORD dwMilliseconds,
                                BOOL bAlertable)
{
    if (bAlertable)
    {
        /* We do not support the bWaitAll case */
        assert (! bWaitAll);
        return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds);
    }
    else
    {
        return WaitForMultipleObjects (nCount, lpHandles, bWaitAll,
                                       dwMilliseconds);
    }
}
#endif

143
144
static vlc_mutex_t super_mutex;
static vlc_cond_t  super_variable;
145
146
147
148
149
150
151
152
153
154

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

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            vlc_mutex_init (&super_mutex);
155
            vlc_cond_init (&super_variable);
156
            vlc_threadvar_create (&thread_key, NULL);
157
158
159
            break;

        case DLL_PROCESS_DETACH:
160
            vlc_threadvar_delete (&thread_key);
161
            vlc_cond_destroy (&super_variable);
162
163
164
165
166
167
168
            vlc_mutex_destroy (&super_mutex);
            break;
    }
    return TRUE;
}

/*** Mutexes ***/
169
void vlc_mutex_init( vlc_mutex_t *p_mutex )
170
171
172
173
{
    /* This creates a recursive mutex. This is OK as fast mutexes have
     * no defined behavior in case of recursive locking. */
    InitializeCriticalSection (&p_mutex->mutex);
174
    p_mutex->dynamic = true;
175
176
}

177
void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
178
179
{
    InitializeCriticalSection( &p_mutex->mutex );
180
    p_mutex->dynamic = true;
181
182
183
184
185
}


void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
{
186
    assert (p_mutex->dynamic);
187
188
189
190
191
    DeleteCriticalSection (&p_mutex->mutex);
}

void vlc_mutex_lock (vlc_mutex_t *p_mutex)
{
192
193
    if (!p_mutex->dynamic)
    {   /* static mutexes */
194
        int canc = vlc_savecancel ();
195
196
197
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
198
        while (p_mutex->locked)
199
200
201
202
203
        {
            p_mutex->contention++;
            vlc_cond_wait (&super_variable, &super_mutex);
            p_mutex->contention--;
        }
204
        p_mutex->locked = true;
205
        vlc_mutex_unlock (&super_mutex);
206
        vlc_restorecancel (canc);
207
        return;
208
    }
209

210
211
212
213
214
    EnterCriticalSection (&p_mutex->mutex);
}

int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
{
215
216
217
    if (!p_mutex->dynamic)
    {   /* static mutexes */
        int ret = EBUSY;
218

219
        assert (p_mutex != &super_mutex); /* this one cannot be static */
220
        vlc_mutex_lock (&super_mutex);
221
222
223
224
225
        if (!p_mutex->locked)
        {
            p_mutex->locked = true;
            ret = 0;
        }
226
        vlc_mutex_unlock (&super_mutex);
227
        return ret;
228
    }
229

230
231
232
233
234
    return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
}

void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
{
235
236
237
238
239
240
241
    if (!p_mutex->dynamic)
    {   /* static mutexes */
        assert (p_mutex != &super_mutex); /* this one cannot be static */

        vlc_mutex_lock (&super_mutex);
        assert (p_mutex->locked);
        p_mutex->locked = false;
242
243
        if (p_mutex->contention)
            vlc_cond_broadcast (&super_variable);
244
245
246
247
        vlc_mutex_unlock (&super_mutex);
        return;
    }

248
249
250
251
    LeaveCriticalSection (&p_mutex->mutex);
}

/*** Condition variables ***/
252
253
254
255
256
257
258
enum
{
    CLOCK_MONOTONIC,
    CLOCK_REALTIME,
};

static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
259
260
{
    /* Create a manual-reset event (manual reset is needed for broadcast). */
261
262
    p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (!p_condvar->handle)
263
        abort();
264
265
266
267
268
269
270
271
272
273
274
    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);
275
276
277
278
}

void vlc_cond_destroy (vlc_cond_t *p_condvar)
{
279
    CloseHandle (p_condvar->handle);
280
281
282
283
284
285
286
287
288
}

void vlc_cond_signal (vlc_cond_t *p_condvar)
{
    /* NOTE: This will cause a broadcast, that is wrong.
     * This will also wake up the next waiting thread if no threads are yet
     * waiting, which is also wrong. However both of these issues are allowed
     * by the provision for spurious wakeups. Better have too many wakeups
     * than too few (= deadlocks). */
289
    SetEvent (p_condvar->handle);
290
291
292
293
}

void vlc_cond_broadcast (vlc_cond_t *p_condvar)
{
294
    SetEvent (p_condvar->handle);
295
296
297
298
299
300
}

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

301
    assert (p_mutex->dynamic); /* TODO */
302
303
304
305
    do
    {
        vlc_testcancel ();
        LeaveCriticalSection (&p_mutex->mutex);
306
        result = WaitForSingleObjectEx (p_condvar->handle, INFINITE, TRUE);
307
308
309
310
311
312
        EnterCriticalSection (&p_mutex->mutex);
    }
    while (result == WAIT_IO_COMPLETION);

    assert (result != WAIT_ABANDONED); /* another thread failed to cleanup! */
    assert (result != WAIT_FAILED);
313
    ResetEvent (p_condvar->handle);
314
315
316
317
318
319
320
}

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

321
    assert (p_mutex->dynamic); /* TODO */
322
323
324
325
    do
    {
        vlc_testcancel ();

326
327
328
329
330
331
332
333
334
335
336
337
338
        mtime_t total;
        switch (p_condvar->clock)
        {
            case CLOCK_MONOTONIC:
                total = mdate();
                break;
            case CLOCK_REALTIME: /* FIXME? sub-second precision */
                total = CLOCK_FREQ * time (NULL);
                break;
            default:
                assert (0);
        }
        total = (deadline - total) / 1000;
339
340
341
342
343
        if( total < 0 )
            total = 0;

        DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
        LeaveCriticalSection (&p_mutex->mutex);
344
        result = WaitForSingleObjectEx (p_condvar->handle, delay, TRUE);
345
346
347
348
349
350
        EnterCriticalSection (&p_mutex->mutex);
    }
    while (result == WAIT_IO_COMPLETION);

    assert (result != WAIT_ABANDONED);
    assert (result != WAIT_FAILED);
351
    ResetEvent (p_condvar->handle);
352
353
354
355

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

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*** 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 ();
        result = WaitForSingleObjectEx (*sem, INFINITE, TRUE);
    }
    while (result == WAIT_IO_COMPLETION);
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
387
388
389
390
391
392
393
394
/*** Read/write locks */
/* SRW (Slim Read Write) locks are available in Vista+ only */
void vlc_rwlock_init (vlc_rwlock_t *lock)
{
    vlc_mutex_init (&lock->mutex);
    vlc_cond_init (&lock->read_wait);
    vlc_cond_init (&lock->write_wait);
    lock->readers = 0; /* active readers */
395
    lock->writers = 0; /* waiting writers */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
396
397
398
399
400
401
402
403
404
405
406
407
408
    lock->writer = 0; /* ID of active writer */
}

void vlc_rwlock_destroy (vlc_rwlock_t *lock)
{
    vlc_cond_destroy (&lock->read_wait);
    vlc_cond_destroy (&lock->write_wait);
    vlc_mutex_destroy (&lock->mutex);
}

void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
409
410
411
412
413
414
415
    /* Recursive read-locking is allowed. With the infos available:
     *  - the loosest possible condition (no active writer) is:
     *     (lock->writer != 0)
     *  - the strictest possible condition is:
     *     (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0))
     *  or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0))
     */
416
    while (lock->writer != 0)
417
418
    {
        assert (lock->readers == 0);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
419
        vlc_cond_wait (&lock->read_wait, &lock->mutex);
420
421
    }
    if (unlikely(lock->readers == ULONG_MAX))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
422
423
424
425
426
        abort ();
    lock->readers++;
    vlc_mutex_unlock (&lock->mutex);
}

427
428
429
430
431
432
433
434
435
436
437
static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
    assert (lock->readers > 0);

    /* If there are no readers left, wake up a writer. */
    if (--lock->readers == 0 && lock->writers > 0)
        vlc_cond_signal (&lock->write_wait);
    vlc_mutex_unlock (&lock->mutex);
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
438
439
440
void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
441
    if (unlikely(lock->writers == ULONG_MAX))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
442
443
        abort ();
    lock->writers++;
444
    /* Wait until nobody owns the lock in either way. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
445
446
    while ((lock->readers > 0) || (lock->writer != 0))
        vlc_cond_wait (&lock->write_wait, &lock->mutex);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447
    lock->writers--;
448
    assert (lock->writer == 0);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
449
450
451
452
    lock->writer = GetCurrentThreadId ();
    vlc_mutex_unlock (&lock->mutex);
}

453
static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
454
455
{
    vlc_mutex_lock (&lock->mutex);
456
457
458
    assert (lock->writer == GetCurrentThreadId ());
    assert (lock->readers == 0);
    lock->writer = 0; /* Write unlock */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
459

460
    /* Let reader and writer compete. Scheduler decides who wins. */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
461
    if (lock->writers > 0)
462
463
        vlc_cond_signal (&lock->write_wait);
    vlc_cond_broadcast (&lock->read_wait);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
464
465
466
    vlc_mutex_unlock (&lock->mutex);
}

467
468
469
470
471
472
473
474
475
476
477
void vlc_rwlock_unlock (vlc_rwlock_t *lock)
{
    /* Note: If the lock is held for reading, lock->writer is nul.
     * If the lock is held for writing, only this thread can store a value to
     * lock->writer. Either way, lock->writer is safe to fetch here. */
    if (lock->writer != 0)
        vlc_rwlock_wrunlock (lock);
    else
        vlc_rwlock_rdunlock (lock);
}

478
/*** Thread-specific variables (TLS) ***/
479
struct vlc_threadvar
480
{
481
482
483
484
485
    DWORD                 id;
    void                (*destroy) (void *);
    struct vlc_threadvar *prev;
    struct vlc_threadvar *next;
} *vlc_threadvar_last = NULL;
Pierre's avatar
Pierre committed
486

487
488
489
490
491
492
493
494
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
495
496
    {
        free (var);
497
        return EAGAIN;
ivoire's avatar
ivoire committed
498
    }
499
500
501
502
503
504
505
506
507
    var->destroy = destr;
    var->next = NULL;
    *p_tls = var;

    vlc_mutex_lock (&super_mutex);
    var->prev = vlc_threadvar_last;
    vlc_threadvar_last = var;
    vlc_mutex_unlock (&super_mutex);
    return 0;
508
509
510
511
}

void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
512
513
514
515
516
517
518
519
520
521
522
523
524
    struct vlc_threadvar *var = *p_tls;

    vlc_mutex_lock (&super_mutex);
    if (var->prev != NULL)
        var->prev->next = var->next;
    else
        vlc_threadvar_last = var->next;
    if (var->next != NULL)
        var->next->prev = var->prev;
    vlc_mutex_unlock (&super_mutex);

    TlsFree (var->id);
    free (var);
525
526
527
528
}

int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
529
    return TlsSetValue (key->id, value) ? ENOMEM : 0;
530
531
532
533
}

void *vlc_threadvar_get (vlc_threadvar_t key)
{
534
535
536
    return TlsGetValue (key->id);
}

537
538
539
540
541
542
/*** Threads ***/
void vlc_threads_setup (libvlc_int_t *p_libvlc)
{
    (void) p_libvlc;
}

543
static void vlc_thread_cleanup (struct vlc_thread *th)
544
545
546
{
    vlc_threadvar_t key;

547
retry:
548
549
550
551
552
    /* 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);
553
        if (value != NULL && key->destroy != NULL)
554
555
556
        {
            vlc_mutex_unlock (&super_mutex);
            vlc_threadvar_set (key, NULL);
557
            key->destroy (value);
558
559
            goto retry;
        }
560
561
    }
    vlc_mutex_unlock (&super_mutex);
562

563
564
565
566
567
568
569
570
    if (th->detached)
    {
        CloseHandle (th->id);
#ifdef UNDER_CE
        CloseHandle (th->cancel_event);
#endif
        free (th);
    }
571
572
}

573
static unsigned __stdcall vlc_entry (void *p)
574
{
575
    struct vlc_thread *th = p;
576

577
578
    vlc_threadvar_set (thread_key, th);
    th->killable = true;
579
    th->data = th->entry (th->data);
580
    vlc_thread_cleanup (th);
581
582
583
    return 0;
}

584
585
static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
                           void *(*entry) (void *), void *data, int priority)
586
{
587
588
    struct vlc_thread *th = malloc (sizeof (*th));
    if (unlikely(th == NULL))
589
        return ENOMEM;
590
591
    th->entry = entry;
    th->data = data;
592
    th->detached = detached;
593
594
595
    th->killable = false; /* not until vlc_entry() ! */
    th->killed = false;
    th->cleaners = NULL;
596

597
    HANDLE hThread;
598
599
600
601
602
#ifndef UNDER_CE
    /* 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) */
603
    hThread = (HANDLE)(uintptr_t)
604
605
        _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
    if (hThread == NULL)
606
    {
607
608
609
        int err = errno;
        free (th);
        return err;
610
    }
611
612

#else
613
614
615
616
    th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (th->cancel_event == NULL)
    {
        free (th);
617
        return ENOMEM;
618
    }
619

620
621
    /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
     * Thread handles act up, too. */
Pierre Ynard's avatar
Pierre Ynard committed
622
    hThread = CreateThread (NULL, 128*1024, vlc_entry, th,
623
624
                            CREATE_SUSPENDED, NULL);
    if (hThread == NULL)
625
626
627
    {
        CloseHandle (th->cancel_event);
        free (th);
628
        return ENOMEM;
629
    }
630
631

#endif
632
    /* Thread is suspended, so we can safely set th->id */
633
    th->id = hThread;
634
635
    if (p_handle != NULL)
        *p_handle = th;
636
637
638
639

    if (priority)
        SetThreadPriority (hThread, priority);

640
641
    ResumeThread (hThread);

642
    return 0;
643
644
}

645
646
647
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
648
    return vlc_clone_attr (p_handle, false, entry, data, priority);
649
650
}

651
void vlc_join (vlc_thread_t th, void **result)
652
653
654
{
    do
        vlc_testcancel ();
655
    while (WaitForSingleObjectEx (th->id, INFINITE, TRUE)
656
657
                                                        == WAIT_IO_COMPLETION);

658
659
    if (result != NULL)
        *result = th->data;
660
    CloseHandle (th->id);
661
#ifdef UNDER_CE
662
    CloseHandle (th->cancel_event);
663
#endif
664
    free (th);
665
666
}

667
668
int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
                      void *data, int priority)
669
{
670
671
672
673
674
    vlc_thread_t th;
    if (p_handle == NULL)
        p_handle = &th;

    return vlc_clone_attr (p_handle, true, entry, data, priority);
675
}
676

677
678
679
680
681
682
683
int vlc_set_priority (vlc_thread_t th, int priority)
{
    if (!SetThreadPriority (th->id, priority))
        return VLC_EGENERIC;
    return VLC_SUCCESS;
}

684
685
686
/*** Thread cancellation ***/

/* APC procedure for thread cancellation */
687
static void CALLBACK vlc_cancel_self (ULONG_PTR self)
688
{
689
    struct vlc_thread *th = (void *)self;
690

691
692
    if (likely(th != NULL))
        th->killed = true;
693
694
}

695
void vlc_cancel (vlc_thread_t th)
696
697
{
#ifndef UNDER_CE
698
    QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
699
#else
700
    SetEvent (th->cancel_event);
701
702
703
704
705
#endif
}

int vlc_savecancel (void)
{
706
707
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
708
709
        return false; /* Main thread - cannot be cancelled anyway */

710
711
    int state = th->killable;
    th->killable = false;
712
713
714
715
716
    return state;
}

void vlc_restorecancel (int state)
{
717
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
718
719
    assert (state == false || state == true);

720
    if (th == NULL)
721
722
        return; /* Main thread - cannot be cancelled anyway */

723
724
    assert (!th->killable);
    th->killable = state != 0;
725
726
727
728
}

void vlc_testcancel (void)
{
729
730
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
731
732
        return; /* Main thread - cannot be cancelled anyway */

733
    if (th->killable && th->killed)
734
    {
735
        for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
736
             p->proc (p->data);
737
738
739

        th->data = NULL; /* TODO: special value? */
        vlc_thread_cleanup (th);
740
#ifndef UNDER_CE
741
        _endthreadex(0);
742
743
744
745
746
747
748
749
750
751
752
753
#else
        ExitThread(0);
#endif
    }
}

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;

754
755
    struct vlc_thread *th = vlc_threadvar_get (thread_key);
    if (th == NULL)
756
757
758
759
760
761
762
763
764
765
        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 *);
766
767
            cleaner->next = th->cleaners;
            th->cleaners = cleaner;
768
769
770
771
772
            break;
        }

        case VLC_CLEANUP_POP:
        {
773
            th->cleaners = th->cleaners->next;
774
775
776
777
778
            break;
        }
    }
    va_end (ap);
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
779
780


781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
/*** Clock ***/
mtime_t mdate (void)
{
    /* We don't need the real date, just the value of a high precision timer */
    LARGE_INTEGER counter, freq;
    if (!QueryPerformanceCounter (&counter)
     || !QueryPerformanceFrequency (&freq))
        abort();

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

    return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
}

#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;
        SleepEx (delay, TRUE);
        vlc_testcancel();
    }
}

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


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
820
/*** Timers ***/
821
822
struct vlc_timer
{
Pierre Ynard's avatar
Pierre Ynard committed
823
#ifndef UNDER_CE
824
    HANDLE handle;
Pierre Ynard's avatar
Pierre Ynard committed
825
826
827
828
#else
    unsigned id;
    unsigned interval;
#endif
829
830
831
832
    void (*func) (void *);
    void *data;
};

Pierre Ynard's avatar
Pierre Ynard committed
833
#ifndef UNDER_CE
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
834
835
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
836
    struct vlc_timer *timer = val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
837
838

    assert (timeout);
839
    timer->func (timer->data);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
840
}
Pierre Ynard's avatar
Pierre Ynard committed
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
#else
static void CALLBACK vlc_timer_do (unsigned timer_id, unsigned msg,
                                   DWORD_PTR user, DWORD_PTR unused1,
                                   DWORD_PTR unused2)
{
    struct vlc_timer *timer = (struct vlc_timer *) user;
    assert (timer_id == timer->id);
    (void) msg;
    (void) unused1;
    (void) unused2;

    timer->func (timer->data);

    if (timer->interval)
    {
        mtime_t interval = timer->interval * 1000;
        vlc_timer_schedule (timer, false, interval, interval);
    }
}
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
861

862
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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;
Pierre Ynard's avatar
Pierre Ynard committed
870
#ifndef UNDER_CE
871
    timer->handle = INVALID_HANDLE_VALUE;
Pierre Ynard's avatar
Pierre Ynard committed
872
873
874
875
#else
    timer->id = 0;
    timer->interval = 0;
#endif
876
    *id = timer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
877
878
879
    return 0;
}

880
void vlc_timer_destroy (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
881
{
Pierre Ynard's avatar
Pierre Ynard committed
882
#ifndef UNDER_CE
883
884
    if (timer->handle != INVALID_HANDLE_VALUE)
        DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
Pierre Ynard's avatar
Pierre Ynard committed
885
886
887
888
889
#else
    if (timer->id)
        timeKillEvent (timer->id);
    /* FIXME: timers that have not yet completed will trigger use-after-free */
#endif
890
    free (timer);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
891
892
}

893
void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
894
895
                         mtime_t value, mtime_t interval)
{
Pierre Ynard's avatar
Pierre Ynard committed
896
#ifndef UNDER_CE
897
    if (timer->handle != INVALID_HANDLE_VALUE)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
898
    {
899
900
        DeleteTimerQueueTimer (NULL, timer->handle, NULL);
        timer->handle = INVALID_HANDLE_VALUE;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
901
    }
Pierre Ynard's avatar
Pierre Ynard committed
902
903
904
905
906
907
908
909
#else
    if (timer->id)
    {
        timeKillEvent (timer->id);
        timer->id = 0;
        timer->interval = 0;
    }
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
910
911
912
913
914
915
916
    if (value == 0)
        return; /* Disarm */

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

#ifndef UNDER_CE
919
920
    if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
                                value, interval, WT_EXECUTEDEFAULT))
Pierre Ynard's avatar
Pierre Ynard committed
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
#else
    TIMECAPS caps;
    timeGetDevCaps (&caps, sizeof(caps));

    unsigned delay = value;
    delay = __MAX(delay, caps.wPeriodMin);
    delay = __MIN(delay, caps.wPeriodMax);

    unsigned event = TIME_ONESHOT;

    if (interval == delay)
        event = TIME_PERIODIC;
    else if (interval)
        timer->interval = interval;

    timer->id = timeSetEvent (delay, delay / 20, vlc_timer_do, (DWORD) timer,
                              event);
    if (!timer->id)
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
940
941
942
        abort ();
}

943
unsigned vlc_timer_getoverrun (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
944
{
945
    (void)timer;
946
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
947
}
948
949
950
951
952
953
954
955
956
957


/*** CPU ***/
unsigned vlc_GetCPUCount (void)
{
#ifndef UNDER_CE
    DWORD process;
    DWORD system;

    if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
958
        return popcount (system);
959
960
961
#endif
     return 1;
}