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