thread.c 21.6 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

static vlc_threadvar_t cancel_key;

/**
 * Per-thread cancellation data
 */
typedef struct vlc_cancel_t
{
    vlc_cleanup_t *cleaners;
#ifdef UNDER_CE
    HANDLE         cancel_event;
#endif
    bool           killable;
    bool           killed;
} vlc_cancel_t;

#ifndef UNDER_CE
# define VLC_CANCEL_INIT { NULL, true, false }
#else
Pierre Ynard's avatar
Pierre Ynard committed
61
# define VLC_CANCEL_INIT { NULL, NULL, true, false }
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 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
#endif

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

static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles,
                                  DWORD delay)
{
    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
    if (nfo == NULL)
    {
        /* Main thread - cannot be cancelled anyway */
        return WaitForMultipleObjects (count, handles, FALSE, delay);
    }
    HANDLE new_handles[count + 1];
    memcpy(new_handles, handles, count * sizeof(HANDLE));
    new_handles[count] = nfo->cancel_event;
    DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE,
                                           delay);
    if (result == WAIT_OBJECT_0 + count)
    {
        vlc_cancel_self (NULL);
        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
vlc_mutex_t super_mutex;
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 157 158 159 160
            vlc_threadvar_create (&cancel_key, free);
            break;

        case DLL_PROCESS_DETACH:
            vlc_threadvar_delete( &cancel_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 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
/*** 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 */
    lock->writers = 0; /* waiting or active writers */
    lock->writer = 0; /* ID of active writer */
}

/**
 * Destroys an initialized unused read/write lock.
 */
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);
}

/**
 * Acquires a read/write lock for reading. Recursion is allowed.
 */
void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
415
    while (lock->writer != 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
        vlc_cond_wait (&lock->read_wait, &lock->mutex);
    if (lock->readers == ULONG_MAX)
        abort ();
    lock->readers++;
    vlc_mutex_unlock (&lock->mutex);
}

/**
 * Acquires a read/write lock for writing. Recursion is not allowed.
 */
void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
    if (lock->writers == ULONG_MAX)
        abort ();
    lock->writers++;
    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
434
    lock->writers--;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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
    lock->writer = GetCurrentThreadId ();
    vlc_mutex_unlock (&lock->mutex);
}

/**
 * Releases a read/write lock.
 */
void vlc_rwlock_unlock (vlc_rwlock_t *lock)
{
    vlc_mutex_lock (&lock->mutex);
    if (lock->readers > 0)
        lock->readers--; /* Read unlock */
    else
        lock->writer = 0; /* Write unlock */

    if (lock->writers > 0)
    {
        if (lock->readers == 0)
            vlc_cond_signal (&lock->write_wait);
    }
    else
        vlc_cond_broadcast (&lock->read_wait);
    vlc_mutex_unlock (&lock->mutex);
}

460 461 462 463
/*** Thread-specific variables (TLS) ***/
int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
{
#warning FIXME: use destr() callback and stop leaking!
464
    VLC_UNUSED( destr );
Pierre's avatar
Pierre committed
465

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    *p_tls = TlsAlloc();
    return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
}

void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
    TlsFree (*p_tls);
}

/**
 * Sets a thread-local variable.
 * @param key thread-local variable key (created with vlc_threadvar_create())
 * @param value new value for the variable for the calling thread
 * @return 0 on success, a system error code otherwise.
 */
int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
    return TlsSetValue (key, value) ? ENOMEM : 0;
}

/**
 * Gets the value of a thread-local variable for the calling thread.
 * This function cannot fail.
 * @return the value associated with the given variable for the calling
 * or NULL if there is no value.
 */
void *vlc_threadvar_get (vlc_threadvar_t key)
{
    return TlsGetValue (key);
}


/*** Threads ***/
499 500 501 502 503
void vlc_threads_setup (libvlc_int_t *p_libvlc)
{
    (void) p_libvlc;
}

504 505
struct vlc_entry_data
{
506 507
    void * (*func) (void *);
    void *  data;
508
    vlc_sem_t ready;
509 510 511
#ifdef UNDER_CE
    HANDLE  cancel_event;
#endif
512 513 514
};

static unsigned __stdcall vlc_entry (void *p)
515
{
516
    struct vlc_entry_data *entry = p;
517
    vlc_cancel_t cancel_data = VLC_CANCEL_INIT;
518 519
    void *(*func) (void *) = entry->func;
    void *data = entry->data;
520

521
#ifdef UNDER_CE
522
    cancel_data.cancel_event = entry->cancel_event;
523 524
#endif
    vlc_threadvar_set (cancel_key, &cancel_data);
525 526
    vlc_sem_post (&entry->ready);
    func (data);
527 528 529 530 531 532
    return 0;
}

int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
               int priority)
{
533
    int err = ENOMEM;
534 535
    HANDLE hThread;

536 537 538 539 540
    struct vlc_entry_data *entry_data = malloc (sizeof (*entry_data));
    if (entry_data == NULL)
        return ENOMEM;
    entry_data->func = entry;
    entry_data->data = data;
541
    vlc_sem_init (&entry_data->ready, 0);
542

543 544 545 546 547
#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) */
548
    hThread = (HANDLE)(uintptr_t)
549
        _beginthreadex (NULL, 0, vlc_entry, entry_data, CREATE_SUSPENDED, NULL);
550 551 552 553 554
    if (! hThread)
    {
        err = errno;
        goto error;
    }
555

556 557 558 559 560
    /* Thread closes the handle when exiting, duplicate it here
     * to be on the safe side when joining. */
    if (!DuplicateHandle (GetCurrentProcess (), hThread,
                          GetCurrentProcess (), p_handle, 0, FALSE,
                          DUPLICATE_SAME_ACCESS))
561
    {
562 563 564 565
        CloseHandle (hThread);
        goto error;
    }

566
#else
567 568 569 570 571 572 573 574 575 576
    vlc_thread_t th = malloc (sizeof (*th));
    if (th == NULL)
        goto error;
    th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (th->cancel_event == NULL)
    {
        free (th);
        goto error;
    }
    entry_data->cancel_event = th->cancel_event;
577

578 579 580 581 582 583 584 585 586
    /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
     * Thread handles act up, too. */
    th->handle = CreateThread (NULL, 128*1024, vlc_entry, entry_data,
                               CREATE_SUSPENDED, NULL);
    if (th->handle == NULL)
    {
        CloseHandle (th->cancel_event);
        free (th);
        goto error;
587
    }
588

589 590 591
    *p_handle = th;
    hThread = th->handle;

592
#endif
593 594 595 596 597

    ResumeThread (hThread);
    if (priority)
        SetThreadPriority (hThread, priority);

598 599 600 601 602 603 604
    /* Prevent cancellation until cancel_data is initialized. */
    /* XXX: This could be postponed to vlc_cancel() or avoided completely by
     * passing the "right" pointer to vlc_cancel_self(). */
    vlc_sem_wait (&entry_data->ready);
    vlc_sem_destroy (&entry_data->ready);
    free (entry_data);

605 606 607
    return 0;

error:
608
    vlc_sem_destroy (&entry_data->ready);
609
    free (entry_data);
610
    return err;
611 612 613 614
}

void vlc_join (vlc_thread_t handle, void **result)
{
615 616 617
#ifdef UNDER_CE
# define handle handle->handle
#endif
618 619
    do
        vlc_testcancel ();
620
    while (WaitForSingleObjectEx (handle, INFINITE, TRUE)
621 622
                                                        == WAIT_IO_COMPLETION);

623
    CloseHandle (handle);
624
    assert (result == NULL); /* <- FIXME if ever needed */
625
#ifdef UNDER_CE
626
# undef handle
627
    CloseHandle (handle->cancel_event);
628
    free (handle);
629 630 631
#endif
}

632 633
void vlc_detach (vlc_thread_t handle)
{
634
#ifndef UNDER_CE
635
    CloseHandle (handle);
636 637 638 639 640
#else
    /* FIXME: handle->cancel_event leak */
    CloseHandle (handle->handle);
    free (handle);
#endif
641
}
642 643 644 645 646 647

/*** Thread cancellation ***/

/* APC procedure for thread cancellation */
static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
{
648 649 650 651 652
    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);

    if (likely(nfo != NULL))
        nfo->killed = true;

653 654 655 656 657 658
    (void)dummy;
}

void vlc_cancel (vlc_thread_t thread_id)
{
#ifndef UNDER_CE
659
    QueueUserAPC (vlc_cancel_self, thread_id, 0);
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
#else
    SetEvent (thread_id->cancel_event);
#endif
}

int vlc_savecancel (void)
{
    int state;

    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
    if (nfo == NULL)
        return false; /* Main thread - cannot be cancelled anyway */

    state = nfo->killable;
    nfo->killable = false;
    return state;
}

void vlc_restorecancel (int state)
{
    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
    assert (state == false || state == true);

    if (nfo == NULL)
        return; /* Main thread - cannot be cancelled anyway */

    assert (!nfo->killable);
    nfo->killable = state != 0;
}

void vlc_testcancel (void)
{
    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
    if (nfo == NULL)
        return; /* Main thread - cannot be cancelled anyway */

    if (nfo->killable && nfo->killed)
    {
        for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
             p->proc (p->data);
#ifndef UNDER_CE
        _endthread ();
#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;

    vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
    if (nfo == NULL)
        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 *);
            cleaner->next = nfo->cleaners;
            nfo->cleaners = cleaner;
            break;
        }

        case VLC_CLEANUP_POP:
        {
            nfo->cleaners = nfo->cleaners->next;
            break;
        }
    }
    va_end (ap);
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
739 740 741


/*** Timers ***/
742 743
struct vlc_timer
{
Pierre Ynard's avatar
Pierre Ynard committed
744
#ifndef UNDER_CE
745
    HANDLE handle;
Pierre Ynard's avatar
Pierre Ynard committed
746 747 748 749
#else
    unsigned id;
    unsigned interval;
#endif
750 751 752 753
    void (*func) (void *);
    void *data;
};

Pierre Ynard's avatar
Pierre Ynard committed
754
#ifndef UNDER_CE
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
755 756
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
757
    struct vlc_timer *timer = val;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
758 759

    assert (timeout);
760
    timer->func (timer->data);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
761
}
Pierre Ynard's avatar
Pierre Ynard committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
#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
782

783
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
784
{
785 786 787 788 789 790
    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
791
#ifndef UNDER_CE
792
    timer->handle = INVALID_HANDLE_VALUE;
Pierre Ynard's avatar
Pierre Ynard committed
793 794 795 796
#else
    timer->id = 0;
    timer->interval = 0;
#endif
797
    *id = timer;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
798 799 800
    return 0;
}

801
void vlc_timer_destroy (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
802
{
Pierre Ynard's avatar
Pierre Ynard committed
803
#ifndef UNDER_CE
804 805
    if (timer->handle != INVALID_HANDLE_VALUE)
        DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
Pierre Ynard's avatar
Pierre Ynard committed
806 807 808 809 810
#else
    if (timer->id)
        timeKillEvent (timer->id);
    /* FIXME: timers that have not yet completed will trigger use-after-free */
#endif
811
    free (timer);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
812 813
}

814
void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
815 816
                         mtime_t value, mtime_t interval)
{
Pierre Ynard's avatar
Pierre Ynard committed
817
#ifndef UNDER_CE
818
    if (timer->handle != INVALID_HANDLE_VALUE)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
819
    {
820 821
        DeleteTimerQueueTimer (NULL, timer->handle, NULL);
        timer->handle = INVALID_HANDLE_VALUE;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
822
    }
Pierre Ynard's avatar
Pierre Ynard committed
823 824 825 826 827 828 829 830
#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
831 832 833 834 835 836 837
    if (value == 0)
        return; /* Disarm */

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

#ifndef UNDER_CE
840 841
    if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
                                value, interval, WT_EXECUTEDEFAULT))
Pierre Ynard's avatar
Pierre Ynard committed
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
#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
861 862 863
        abort ();
}

864
unsigned vlc_timer_getoverrun (vlc_timer_t timer)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
865
{
866
    (void)timer;
867
    return 0;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
868
}