]> git.sesse.net Git - vlc/blob - src/misc/w32thread.c
234ef8becbef3b9f06a1ecbdeb35f7024df9fe91
[vlc] / src / misc / w32thread.c
1 /*****************************************************************************
2  * w32thread.c : Win32 back-end for LibVLC
3  *****************************************************************************
4  * Copyright (C) 1999-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *          Clément Sténac
11  *          Rémi Denis-Courmont
12  *          Pierre Ynard
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34
35 #include "libvlc.h"
36 #include <stdarg.h>
37 #include <assert.h>
38 #include <limits.h>
39 #include <errno.h>
40 #ifdef UNDER_CE
41 # include <mmsystem.h>
42 #endif
43
44 static vlc_threadvar_t cancel_key;
45
46 /**
47  * Per-thread cancellation data
48  */
49 typedef struct vlc_cancel_t
50 {
51     vlc_cleanup_t *cleaners;
52 #ifdef UNDER_CE
53     HANDLE         cancel_event;
54 #endif
55     bool           killable;
56     bool           killed;
57 } vlc_cancel_t;
58
59 #ifndef UNDER_CE
60 # define VLC_CANCEL_INIT { NULL, true, false }
61 #else
62 # define VLC_CANCEL_INIT { NULL, NULL, true, false }
63 #endif
64
65 #ifdef UNDER_CE
66 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy);
67
68 static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles,
69                                   DWORD delay)
70 {
71     vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
72     if (nfo == NULL)
73     {
74         /* Main thread - cannot be cancelled anyway */
75         return WaitForMultipleObjects (count, handles, FALSE, delay);
76     }
77     HANDLE new_handles[count + 1];
78     memcpy(new_handles, handles, count * sizeof(HANDLE));
79     new_handles[count] = nfo->cancel_event;
80     DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE,
81                                            delay);
82     if (result == WAIT_OBJECT_0 + count)
83     {
84         vlc_cancel_self (NULL);
85         return WAIT_IO_COMPLETION;
86     }
87     else
88     {
89         return result;
90     }
91 }
92
93 DWORD SleepEx (DWORD dwMilliseconds, BOOL bAlertable)
94 {
95     if (bAlertable)
96     {
97         DWORD result = vlc_cancelable_wait (0, NULL, dwMilliseconds);
98         return (result == WAIT_TIMEOUT) ? 0 : WAIT_IO_COMPLETION;
99     }
100     else
101     {
102         Sleep(dwMilliseconds);
103         return 0;
104     }
105 }
106
107 DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds,
108                              BOOL bAlertable)
109 {
110     if (bAlertable)
111     {
112         /* The MSDN documentation specifies different return codes,
113          * but in practice they are the same. We just check that it
114          * remains so. */
115 #if WAIT_ABANDONED != WAIT_ABANDONED_0
116 # error Windows headers changed, code needs to be rewritten!
117 #endif
118         return vlc_cancelable_wait (1, &hHandle, dwMilliseconds);
119     }
120     else
121     {
122         return WaitForSingleObject (hHandle, dwMilliseconds);
123     }
124 }
125
126 DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles,
127                                 BOOL bWaitAll, DWORD dwMilliseconds,
128                                 BOOL bAlertable)
129 {
130     if (bAlertable)
131     {
132         /* We do not support the bWaitAll case */
133         assert (! bWaitAll);
134         return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds);
135     }
136     else
137     {
138         return WaitForMultipleObjects (nCount, lpHandles, bWaitAll,
139                                        dwMilliseconds);
140     }
141 }
142 #endif
143
144 vlc_mutex_t super_mutex;
145 vlc_cond_t  super_variable;
146
147 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
148 {
149     (void) hinstDll;
150     (void) lpvReserved;
151
152     switch (fdwReason)
153     {
154         case DLL_PROCESS_ATTACH:
155             vlc_mutex_init (&super_mutex);
156             vlc_cond_init (&super_variable);
157             vlc_threadvar_create (&cancel_key, free);
158             break;
159
160         case DLL_PROCESS_DETACH:
161             vlc_threadvar_delete( &cancel_key );
162             vlc_cond_destroy (&super_variable);
163             vlc_mutex_destroy (&super_mutex);
164             break;
165     }
166     return TRUE;
167 }
168
169 /*** Mutexes ***/
170 void vlc_mutex_init( vlc_mutex_t *p_mutex )
171 {
172     /* This creates a recursive mutex. This is OK as fast mutexes have
173      * no defined behavior in case of recursive locking. */
174     InitializeCriticalSection (&p_mutex->mutex);
175     p_mutex->dynamic = true;
176 }
177
178 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
179 {
180     InitializeCriticalSection( &p_mutex->mutex );
181     p_mutex->dynamic = true;
182 }
183
184
185 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
186 {
187     assert (p_mutex->dynamic);
188     DeleteCriticalSection (&p_mutex->mutex);
189 }
190
191 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
192 {
193     if (!p_mutex->dynamic)
194     {   /* static mutexes */
195         assert (p_mutex != &super_mutex); /* this one cannot be static */
196
197         vlc_mutex_lock (&super_mutex);
198         while (p_mutex->locked)
199         {
200             p_mutex->contention++;
201             vlc_cond_wait (&super_variable, &super_mutex);
202             p_mutex->contention--;
203         }
204         p_mutex->locked = true;
205         vlc_mutex_unlock (&super_mutex);
206         return;
207     }
208
209     EnterCriticalSection (&p_mutex->mutex);
210 }
211
212 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
213 {
214     if (!p_mutex->dynamic)
215     {   /* static mutexes */
216         int ret = EBUSY;
217
218         assert (p_mutex != &super_mutex); /* this one cannot be static */
219         vlc_mutex_lock (&super_mutex);
220         if (!p_mutex->locked)
221         {
222             p_mutex->locked = true;
223             ret = 0;
224         }
225         vlc_mutex_unlock (&super_mutex);
226         return ret;
227     }
228
229     return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
230 }
231
232 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
233 {
234     if (!p_mutex->dynamic)
235     {   /* static mutexes */
236         assert (p_mutex != &super_mutex); /* this one cannot be static */
237
238         vlc_mutex_lock (&super_mutex);
239         assert (p_mutex->locked);
240         p_mutex->locked = false;
241         if (p_mutex->contention)
242             vlc_cond_broadcast (&super_variable);
243         vlc_mutex_unlock (&super_mutex);
244         return;
245     }
246
247     LeaveCriticalSection (&p_mutex->mutex);
248 }
249
250 /*** Condition variables ***/
251 void vlc_cond_init( vlc_cond_t *p_condvar )
252 {
253     /* Create a manual-reset event (manual reset is needed for broadcast). */
254     *p_condvar = CreateEvent (NULL, TRUE, FALSE, NULL);
255     if (!*p_condvar)
256         abort();
257 }
258
259 void vlc_cond_destroy (vlc_cond_t *p_condvar)
260 {
261     CloseHandle (*p_condvar);
262 }
263
264 void vlc_cond_signal (vlc_cond_t *p_condvar)
265 {
266     /* NOTE: This will cause a broadcast, that is wrong.
267      * This will also wake up the next waiting thread if no threads are yet
268      * waiting, which is also wrong. However both of these issues are allowed
269      * by the provision for spurious wakeups. Better have too many wakeups
270      * than too few (= deadlocks). */
271     SetEvent (*p_condvar);
272 }
273
274 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
275 {
276     SetEvent (*p_condvar);
277 }
278
279 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
280 {
281     DWORD result;
282
283     assert (p_mutex->dynamic); /* TODO */
284     do
285     {
286         vlc_testcancel ();
287         LeaveCriticalSection (&p_mutex->mutex);
288         result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE);
289         EnterCriticalSection (&p_mutex->mutex);
290     }
291     while (result == WAIT_IO_COMPLETION);
292
293     assert (result != WAIT_ABANDONED); /* another thread failed to cleanup! */
294     assert (result != WAIT_FAILED);
295     ResetEvent (*p_condvar);
296 }
297
298 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
299                         mtime_t deadline)
300 {
301     DWORD result;
302
303     assert (p_mutex->dynamic); /* TODO */
304     do
305     {
306         vlc_testcancel ();
307
308         mtime_t total = (deadline - mdate ())/1000;
309         if( total < 0 )
310             total = 0;
311
312         DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
313         LeaveCriticalSection (&p_mutex->mutex);
314         result = WaitForSingleObjectEx (*p_condvar, delay, TRUE);
315         EnterCriticalSection (&p_mutex->mutex);
316     }
317     while (result == WAIT_IO_COMPLETION);
318
319     assert (result != WAIT_ABANDONED);
320     assert (result != WAIT_FAILED);
321     ResetEvent (*p_condvar);
322
323     return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
324 }
325
326 /*** Semaphore ***/
327 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
328 {
329     *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
330     if (*sem == NULL)
331         abort ();
332 }
333
334 void vlc_sem_destroy (vlc_sem_t *sem)
335 {
336     CloseHandle (*sem);
337 }
338
339 int vlc_sem_post (vlc_sem_t *sem)
340 {
341     ReleaseSemaphore (*sem, 1, NULL);
342     return 0; /* FIXME */
343 }
344
345 void vlc_sem_wait (vlc_sem_t *sem)
346 {
347     DWORD result;
348
349     do
350     {
351         vlc_testcancel ();
352         result = WaitForSingleObjectEx (*sem, INFINITE, TRUE);
353     }
354     while (result == WAIT_IO_COMPLETION);
355 }
356
357 /*** Read/write locks */
358 /* SRW (Slim Read Write) locks are available in Vista+ only */
359 void vlc_rwlock_init (vlc_rwlock_t *lock)
360 {
361     vlc_mutex_init (&lock->mutex);
362     vlc_cond_init (&lock->read_wait);
363     vlc_cond_init (&lock->write_wait);
364     lock->readers = 0; /* active readers */
365     lock->writers = 0; /* waiting or active writers */
366     lock->writer = 0; /* ID of active writer */
367 }
368
369 /**
370  * Destroys an initialized unused read/write lock.
371  */
372 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
373 {
374     vlc_cond_destroy (&lock->read_wait);
375     vlc_cond_destroy (&lock->write_wait);
376     vlc_mutex_destroy (&lock->mutex);
377 }
378
379 /**
380  * Acquires a read/write lock for reading. Recursion is allowed.
381  */
382 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
383 {
384     vlc_mutex_lock (&lock->mutex);
385     while (lock->writer != 0)
386         vlc_cond_wait (&lock->read_wait, &lock->mutex);
387     if (lock->readers == ULONG_MAX)
388         abort ();
389     lock->readers++;
390     vlc_mutex_unlock (&lock->mutex);
391 }
392
393 /**
394  * Acquires a read/write lock for writing. Recursion is not allowed.
395  */
396 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
397 {
398     vlc_mutex_lock (&lock->mutex);
399     if (lock->writers == ULONG_MAX)
400         abort ();
401     lock->writers++;
402     while ((lock->readers > 0) || (lock->writer != 0))
403         vlc_cond_wait (&lock->write_wait, &lock->mutex);
404     lock->writers--;
405     lock->writer = GetCurrentThreadId ();
406     vlc_mutex_unlock (&lock->mutex);
407 }
408
409 /**
410  * Releases a read/write lock.
411  */
412 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
413 {
414     vlc_mutex_lock (&lock->mutex);
415     if (lock->readers > 0)
416         lock->readers--; /* Read unlock */
417     else
418         lock->writer = 0; /* Write unlock */
419
420     if (lock->writers > 0)
421     {
422         if (lock->readers == 0)
423             vlc_cond_signal (&lock->write_wait);
424     }
425     else
426         vlc_cond_broadcast (&lock->read_wait);
427     vlc_mutex_unlock (&lock->mutex);
428 }
429
430 /*** Thread-specific variables (TLS) ***/
431 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
432 {
433 #warning FIXME: use destr() callback and stop leaking!
434     VLC_UNUSED( destr );
435
436     *p_tls = TlsAlloc();
437     return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
438 }
439
440 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
441 {
442     TlsFree (*p_tls);
443 }
444
445 /**
446  * Sets a thread-local variable.
447  * @param key thread-local variable key (created with vlc_threadvar_create())
448  * @param value new value for the variable for the calling thread
449  * @return 0 on success, a system error code otherwise.
450  */
451 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
452 {
453     return TlsSetValue (key, value) ? ENOMEM : 0;
454 }
455
456 /**
457  * Gets the value of a thread-local variable for the calling thread.
458  * This function cannot fail.
459  * @return the value associated with the given variable for the calling
460  * or NULL if there is no value.
461  */
462 void *vlc_threadvar_get (vlc_threadvar_t key)
463 {
464     return TlsGetValue (key);
465 }
466
467
468 /*** Threads ***/
469 void vlc_threads_setup (libvlc_int_t *p_libvlc)
470 {
471     (void) p_libvlc;
472 }
473
474 struct vlc_entry_data
475 {
476     void * (*func) (void *);
477     void *  data;
478 #ifdef UNDER_CE
479     HANDLE  cancel_event;
480 #endif
481 };
482
483 static unsigned __stdcall vlc_entry (void *p)
484 {
485     vlc_cancel_t cancel_data = VLC_CANCEL_INIT;
486     struct vlc_entry_data data;
487
488     memcpy (&data, p, sizeof (data));
489     free (p);
490
491 #ifdef UNDER_CE
492     cancel_data.cancel_event = data.cancel_event;
493 #endif
494
495     vlc_threadvar_set (cancel_key, &cancel_data);
496     data.func (data.data);
497     return 0;
498 }
499
500 int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
501                int priority)
502 {
503     int err = ENOMEM;
504     HANDLE hThread;
505
506     struct vlc_entry_data *entry_data = malloc (sizeof (*entry_data));
507     if (entry_data == NULL)
508         return ENOMEM;
509     entry_data->func = entry;
510     entry_data->data = data;
511
512 #ifndef UNDER_CE
513     /* When using the MSVCRT C library you have to use the _beginthreadex
514      * function instead of CreateThread, otherwise you'll end up with
515      * memory leaks and the signal functions not working (see Microsoft
516      * Knowledge Base, article 104641) */
517     hThread = (HANDLE)(uintptr_t)
518         _beginthreadex (NULL, 0, vlc_entry, entry_data, CREATE_SUSPENDED, NULL);
519     if (! hThread)
520     {
521         err = errno;
522         goto error;
523     }
524
525     /* Thread closes the handle when exiting, duplicate it here
526      * to be on the safe side when joining. */
527     if (!DuplicateHandle (GetCurrentProcess (), hThread,
528                           GetCurrentProcess (), p_handle, 0, FALSE,
529                           DUPLICATE_SAME_ACCESS))
530     {
531         CloseHandle (hThread);
532         goto error;
533     }
534
535 #else
536     vlc_thread_t th = malloc (sizeof (*th));
537     if (th == NULL)
538         goto error;
539     th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
540     if (th->cancel_event == NULL)
541     {
542         free (th);
543         goto error;
544     }
545     entry_data->cancel_event = th->cancel_event;
546
547     /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
548      * Thread handles act up, too. */
549     th->handle = CreateThread (NULL, 128*1024, vlc_entry, entry_data,
550                                CREATE_SUSPENDED, NULL);
551     if (th->handle == NULL)
552     {
553         CloseHandle (th->cancel_event);
554         free (th);
555         goto error;
556     }
557
558     *p_handle = th;
559     hThread = th->handle;
560
561 #endif
562
563     ResumeThread (hThread);
564     if (priority)
565         SetThreadPriority (hThread, priority);
566
567     return 0;
568
569 error:
570     free (entry_data);
571     return err;
572 }
573
574 void vlc_join (vlc_thread_t handle, void **result)
575 {
576 #ifdef UNDER_CE
577 # define handle handle->handle
578 #endif
579     do
580         vlc_testcancel ();
581     while (WaitForSingleObjectEx (handle, INFINITE, TRUE)
582                                                         == WAIT_IO_COMPLETION);
583
584     CloseHandle (handle);
585     assert (result == NULL); /* <- FIXME if ever needed */
586 #ifdef UNDER_CE
587 # undef handle
588     CloseHandle (handle->cancel_event);
589     free (handle);
590 #endif
591 }
592
593 void vlc_detach (vlc_thread_t handle)
594 {
595 #ifndef UNDER_CE
596     CloseHandle (handle);
597 #else
598     /* FIXME: handle->cancel_event leak */
599     CloseHandle (handle->handle);
600     free (handle);
601 #endif
602 }
603
604 /*** Thread cancellation ***/
605
606 /* APC procedure for thread cancellation */
607 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
608 {
609     (void)dummy;
610     vlc_control_cancel (VLC_DO_CANCEL);
611 }
612
613 void vlc_cancel (vlc_thread_t thread_id)
614 {
615 #ifndef UNDER_CE
616     QueueUserAPC (vlc_cancel_self, thread_id, 0);
617 #else
618     SetEvent (thread_id->cancel_event);
619 #endif
620 }
621
622 int vlc_savecancel (void)
623 {
624     int state;
625
626     vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
627     if (nfo == NULL)
628         return false; /* Main thread - cannot be cancelled anyway */
629
630     state = nfo->killable;
631     nfo->killable = false;
632     return state;
633 }
634
635 void vlc_restorecancel (int state)
636 {
637     vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
638     assert (state == false || state == true);
639
640     if (nfo == NULL)
641         return; /* Main thread - cannot be cancelled anyway */
642
643     assert (!nfo->killable);
644     nfo->killable = state != 0;
645 }
646
647 void vlc_testcancel (void)
648 {
649     vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
650     if (nfo == NULL)
651         return; /* Main thread - cannot be cancelled anyway */
652
653     if (nfo->killable && nfo->killed)
654     {
655         for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
656              p->proc (p->data);
657 #ifndef UNDER_CE
658         _endthread ();
659 #else
660         ExitThread(0);
661 #endif
662     }
663 }
664
665 void vlc_control_cancel (int cmd, ...)
666 {
667     /* NOTE: This function only modifies thread-specific data, so there is no
668      * need to lock anything. */
669     va_list ap;
670
671     vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
672     if (nfo == NULL)
673         return; /* Main thread - cannot be cancelled anyway */
674
675     va_start (ap, cmd);
676     switch (cmd)
677     {
678         case VLC_DO_CANCEL:
679             nfo->killed = true;
680             break;
681
682         case VLC_CLEANUP_PUSH:
683         {
684             /* cleaner is a pointer to the caller stack, no need to allocate
685              * and copy anything. As a nice side effect, this cannot fail. */
686             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
687             cleaner->next = nfo->cleaners;
688             nfo->cleaners = cleaner;
689             break;
690         }
691
692         case VLC_CLEANUP_POP:
693         {
694             nfo->cleaners = nfo->cleaners->next;
695             break;
696         }
697     }
698     va_end (ap);
699 }
700
701
702 /*** Timers ***/
703 struct vlc_timer
704 {
705 #ifndef UNDER_CE
706     HANDLE handle;
707 #else
708     unsigned id;
709     unsigned interval;
710 #endif
711     void (*func) (void *);
712     void *data;
713 };
714
715 #ifndef UNDER_CE
716 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
717 {
718     struct vlc_timer *timer = val;
719
720     assert (timeout);
721     timer->func (timer->data);
722 }
723 #else
724 static void CALLBACK vlc_timer_do (unsigned timer_id, unsigned msg,
725                                    DWORD_PTR user, DWORD_PTR unused1,
726                                    DWORD_PTR unused2)
727 {
728     struct vlc_timer *timer = (struct vlc_timer *) user;
729     assert (timer_id == timer->id);
730     (void) msg;
731     (void) unused1;
732     (void) unused2;
733
734     timer->func (timer->data);
735
736     if (timer->interval)
737     {
738         mtime_t interval = timer->interval * 1000;
739         vlc_timer_schedule (timer, false, interval, interval);
740     }
741 }
742 #endif
743
744 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
745 {
746     struct vlc_timer *timer = malloc (sizeof (*timer));
747
748     if (timer == NULL)
749         return ENOMEM;
750     timer->func = func;
751     timer->data = data;
752 #ifndef UNDER_CE
753     timer->handle = INVALID_HANDLE_VALUE;
754 #else
755     timer->id = 0;
756     timer->interval = 0;
757 #endif
758     *id = timer;
759     return 0;
760 }
761
762 void vlc_timer_destroy (vlc_timer_t timer)
763 {
764 #ifndef UNDER_CE
765     if (timer->handle != INVALID_HANDLE_VALUE)
766         DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
767 #else
768     if (timer->id)
769         timeKillEvent (timer->id);
770     /* FIXME: timers that have not yet completed will trigger use-after-free */
771 #endif
772     free (timer);
773 }
774
775 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
776                          mtime_t value, mtime_t interval)
777 {
778 #ifndef UNDER_CE
779     if (timer->handle != INVALID_HANDLE_VALUE)
780     {
781         DeleteTimerQueueTimer (NULL, timer->handle, NULL);
782         timer->handle = INVALID_HANDLE_VALUE;
783     }
784 #else
785     if (timer->id)
786     {
787         timeKillEvent (timer->id);
788         timer->id = 0;
789         timer->interval = 0;
790     }
791 #endif
792     if (value == 0)
793         return; /* Disarm */
794
795     if (absolute)
796         value -= mdate ();
797     value = (value + 999) / 1000;
798     interval = (interval + 999) / 1000;
799
800 #ifndef UNDER_CE
801     if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
802                                 value, interval, WT_EXECUTEDEFAULT))
803 #else
804     TIMECAPS caps;
805     timeGetDevCaps (&caps, sizeof(caps));
806
807     unsigned delay = value;
808     delay = __MAX(delay, caps.wPeriodMin);
809     delay = __MIN(delay, caps.wPeriodMax);
810
811     unsigned event = TIME_ONESHOT;
812
813     if (interval == delay)
814         event = TIME_PERIODIC;
815     else if (interval)
816         timer->interval = interval;
817
818     timer->id = timeSetEvent (delay, delay / 20, vlc_timer_do, (DWORD) timer,
819                               event);
820     if (!timer->id)
821 #endif
822         abort ();
823 }
824
825 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
826 {
827     (void)timer;
828     return 0;
829 }