Win32/WinCE: always use our internal wait wrapper
[vlc.git] / 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 thread_key;
44
45 /**
46  * Per-thread data
47  */
48 struct vlc_thread
49 {
50     HANDLE         id;
51 #ifdef UNDER_CE
52     HANDLE         cancel_event;
53 #endif
54
55     bool           detached;
56     bool           killable;
57     bool           killed;
58     vlc_cleanup_t *cleaners;
59
60     void        *(*entry) (void *);
61     void          *data;
62 };
63
64 static vlc_mutex_t super_mutex;
65 static vlc_cond_t  super_variable;
66
67 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
68
69 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
70 {
71     (void) hinstDll;
72     (void) lpvReserved;
73
74     switch (fdwReason)
75     {
76         case DLL_PROCESS_ATTACH:
77             vlc_mutex_init (&super_mutex);
78             vlc_cond_init (&super_variable);
79             vlc_threadvar_create (&thread_key, NULL);
80             break;
81
82         case DLL_PROCESS_DETACH:
83             vlc_threadvar_delete (&thread_key);
84             vlc_cond_destroy (&super_variable);
85             vlc_mutex_destroy (&super_mutex);
86             break;
87     }
88     return TRUE;
89 }
90
91 static void CALLBACK vlc_cancel_self (ULONG_PTR);
92
93 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
94                                          DWORD delay)
95 {
96     DWORD ret;
97 #ifdef UNDER_CE
98     HANDLE buf[count + 1];
99
100     struct vlc_thread *th = vlc_threadvar_get (thread_key);
101     if (th != NULL)
102     {
103         memcpy (buf, handles, count * sizeof(HANDLE));
104         buf[count++] = th->cancel_event;
105         handles = buf;
106     }
107
108     if (count == 0)
109     {
110          Sleep (delay);
111          ret = WAIT_TIMEOUT;
112     }
113     else
114         ret = WaitForMultipleObjects (count, handles, FALSE, delay);
115
116     if ((th != NULL) && (ret == WAIT_OBJECT_0 + count - 1))
117     {
118         vlc_cancel_self ((uintptr_t)th);
119         ret = WAIT_IO_COMPLETION;
120     }
121 #else
122     if (count == 0)
123     {
124         ret = SleepEx (delay, TRUE);
125         if (ret == 0)
126             ret = WAIT_TIMEOUT;
127     }
128     else
129         ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
130 #endif
131     /* We do not abandon objects... this would be a bug */
132     assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
133
134     if (unlikely(ret == WAIT_FAILED))
135         abort (); /* We are screwed! */
136     return ret;
137 }
138
139 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
140 {
141     return vlc_WaitForMultipleObjects (1, &handle, delay);
142 }
143
144 static DWORD vlc_Sleep (DWORD delay)
145 {
146     DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
147     return (ret != WAIT_TIMEOUT) ? ret : 0;
148 }
149
150
151 /*** Mutexes ***/
152 void vlc_mutex_init( vlc_mutex_t *p_mutex )
153 {
154     /* This creates a recursive mutex. This is OK as fast mutexes have
155      * no defined behavior in case of recursive locking. */
156     InitializeCriticalSection (&p_mutex->mutex);
157     p_mutex->dynamic = true;
158 }
159
160 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
161 {
162     InitializeCriticalSection( &p_mutex->mutex );
163     p_mutex->dynamic = true;
164 }
165
166
167 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
168 {
169     assert (p_mutex->dynamic);
170     DeleteCriticalSection (&p_mutex->mutex);
171 }
172
173 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
174 {
175     if (!p_mutex->dynamic)
176     {   /* static mutexes */
177         int canc = vlc_savecancel ();
178         assert (p_mutex != &super_mutex); /* this one cannot be static */
179
180         vlc_mutex_lock (&super_mutex);
181         while (p_mutex->locked)
182         {
183             p_mutex->contention++;
184             vlc_cond_wait (&super_variable, &super_mutex);
185             p_mutex->contention--;
186         }
187         p_mutex->locked = true;
188         vlc_mutex_unlock (&super_mutex);
189         vlc_restorecancel (canc);
190         return;
191     }
192
193     EnterCriticalSection (&p_mutex->mutex);
194 }
195
196 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
197 {
198     if (!p_mutex->dynamic)
199     {   /* static mutexes */
200         int ret = EBUSY;
201
202         assert (p_mutex != &super_mutex); /* this one cannot be static */
203         vlc_mutex_lock (&super_mutex);
204         if (!p_mutex->locked)
205         {
206             p_mutex->locked = true;
207             ret = 0;
208         }
209         vlc_mutex_unlock (&super_mutex);
210         return ret;
211     }
212
213     return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
214 }
215
216 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
217 {
218     if (!p_mutex->dynamic)
219     {   /* static mutexes */
220         assert (p_mutex != &super_mutex); /* this one cannot be static */
221
222         vlc_mutex_lock (&super_mutex);
223         assert (p_mutex->locked);
224         p_mutex->locked = false;
225         if (p_mutex->contention)
226             vlc_cond_broadcast (&super_variable);
227         vlc_mutex_unlock (&super_mutex);
228         return;
229     }
230
231     LeaveCriticalSection (&p_mutex->mutex);
232 }
233
234 /*** Condition variables ***/
235 enum
236 {
237     CLOCK_MONOTONIC,
238     CLOCK_REALTIME,
239 };
240
241 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
242 {
243     /* Create a manual-reset event (manual reset is needed for broadcast). */
244     p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
245     if (!p_condvar->handle)
246         abort();
247     p_condvar->clock = clock;
248 }
249
250 void vlc_cond_init (vlc_cond_t *p_condvar)
251 {
252     vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
253 }
254
255 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
256 {
257     vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
258 }
259
260 void vlc_cond_destroy (vlc_cond_t *p_condvar)
261 {
262     CloseHandle (p_condvar->handle);
263 }
264
265 void vlc_cond_signal (vlc_cond_t *p_condvar)
266 {
267     /* NOTE: This will cause a broadcast, that is wrong.
268      * This will also wake up the next waiting thread if no threads are yet
269      * waiting, which is also wrong. However both of these issues are allowed
270      * by the provision for spurious wakeups. Better have too many wakeups
271      * than too few (= deadlocks). */
272     SetEvent (p_condvar->handle);
273 }
274
275 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
276 {
277     SetEvent (p_condvar->handle);
278 }
279
280 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
281 {
282     DWORD result;
283
284     assert (p_mutex->dynamic); /* TODO */
285     do
286     {
287         vlc_testcancel ();
288         LeaveCriticalSection (&p_mutex->mutex);
289         result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
290         EnterCriticalSection (&p_mutex->mutex);
291     }
292     while (result == WAIT_IO_COMPLETION);
293
294     ResetEvent (p_condvar->handle);
295 }
296
297 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
298                         mtime_t deadline)
299 {
300     DWORD result;
301
302     assert (p_mutex->dynamic); /* TODO */
303     do
304     {
305         vlc_testcancel ();
306
307         mtime_t total;
308         switch (p_condvar->clock)
309         {
310             case CLOCK_REALTIME: /* FIXME? sub-second precision */
311                 total = CLOCK_FREQ * time (NULL);
312                 break;
313             default:
314                 assert (p_condvar->clock == CLOCK_MONOTONIC);
315                 total = mdate();
316                 break;
317         }
318         total = (deadline - total) / 1000;
319         if( total < 0 )
320             total = 0;
321
322         DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
323         LeaveCriticalSection (&p_mutex->mutex);
324         result = vlc_WaitForSingleObject (p_condvar->handle, delay);
325         EnterCriticalSection (&p_mutex->mutex);
326     }
327     while (result == WAIT_IO_COMPLETION);
328
329     ResetEvent (p_condvar->handle);
330
331     return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
332 }
333
334 /*** Semaphore ***/
335 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
336 {
337     *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
338     if (*sem == NULL)
339         abort ();
340 }
341
342 void vlc_sem_destroy (vlc_sem_t *sem)
343 {
344     CloseHandle (*sem);
345 }
346
347 int vlc_sem_post (vlc_sem_t *sem)
348 {
349     ReleaseSemaphore (*sem, 1, NULL);
350     return 0; /* FIXME */
351 }
352
353 void vlc_sem_wait (vlc_sem_t *sem)
354 {
355     DWORD result;
356
357     do
358     {
359         vlc_testcancel ();
360         result = vlc_WaitForSingleObject (*sem, INFINITE);
361     }
362     while (result == WAIT_IO_COMPLETION);
363 }
364
365 /*** Read/write locks */
366 /* SRW (Slim Read Write) locks are available in Vista+ only */
367 void vlc_rwlock_init (vlc_rwlock_t *lock)
368 {
369     vlc_mutex_init (&lock->mutex);
370     vlc_cond_init (&lock->read_wait);
371     vlc_cond_init (&lock->write_wait);
372     lock->readers = 0; /* active readers */
373     lock->writers = 0; /* waiting writers */
374     lock->writer = 0; /* ID of active writer */
375 }
376
377 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
378 {
379     vlc_cond_destroy (&lock->read_wait);
380     vlc_cond_destroy (&lock->write_wait);
381     vlc_mutex_destroy (&lock->mutex);
382 }
383
384 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
385 {
386     vlc_mutex_lock (&lock->mutex);
387     /* Recursive read-locking is allowed. With the infos available:
388      *  - the loosest possible condition (no active writer) is:
389      *     (lock->writer != 0)
390      *  - the strictest possible condition is:
391      *     (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0))
392      *  or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0))
393      */
394     while (lock->writer != 0)
395     {
396         assert (lock->readers == 0);
397         vlc_cond_wait (&lock->read_wait, &lock->mutex);
398     }
399     if (unlikely(lock->readers == ULONG_MAX))
400         abort ();
401     lock->readers++;
402     vlc_mutex_unlock (&lock->mutex);
403 }
404
405 static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock)
406 {
407     vlc_mutex_lock (&lock->mutex);
408     assert (lock->readers > 0);
409
410     /* If there are no readers left, wake up a writer. */
411     if (--lock->readers == 0 && lock->writers > 0)
412         vlc_cond_signal (&lock->write_wait);
413     vlc_mutex_unlock (&lock->mutex);
414 }
415
416 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
417 {
418     vlc_mutex_lock (&lock->mutex);
419     if (unlikely(lock->writers == ULONG_MAX))
420         abort ();
421     lock->writers++;
422     /* Wait until nobody owns the lock in either way. */
423     while ((lock->readers > 0) || (lock->writer != 0))
424         vlc_cond_wait (&lock->write_wait, &lock->mutex);
425     lock->writers--;
426     assert (lock->writer == 0);
427     lock->writer = GetCurrentThreadId ();
428     vlc_mutex_unlock (&lock->mutex);
429 }
430
431 static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
432 {
433     vlc_mutex_lock (&lock->mutex);
434     assert (lock->writer == GetCurrentThreadId ());
435     assert (lock->readers == 0);
436     lock->writer = 0; /* Write unlock */
437
438     /* Let reader and writer compete. Scheduler decides who wins. */
439     if (lock->writers > 0)
440         vlc_cond_signal (&lock->write_wait);
441     vlc_cond_broadcast (&lock->read_wait);
442     vlc_mutex_unlock (&lock->mutex);
443 }
444
445 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
446 {
447     /* Note: If the lock is held for reading, lock->writer is nul.
448      * If the lock is held for writing, only this thread can store a value to
449      * lock->writer. Either way, lock->writer is safe to fetch here. */
450     if (lock->writer != 0)
451         vlc_rwlock_wrunlock (lock);
452     else
453         vlc_rwlock_rdunlock (lock);
454 }
455
456 /*** Thread-specific variables (TLS) ***/
457 struct vlc_threadvar
458 {
459     DWORD                 id;
460     void                (*destroy) (void *);
461     struct vlc_threadvar *prev;
462     struct vlc_threadvar *next;
463 } *vlc_threadvar_last = NULL;
464
465 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
466 {
467     struct vlc_threadvar *var = malloc (sizeof (*var));
468     if (unlikely(var == NULL))
469         return errno;
470
471     var->id = TlsAlloc();
472     if (var->id == TLS_OUT_OF_INDEXES)
473     {
474         free (var);
475         return EAGAIN;
476     }
477     var->destroy = destr;
478     var->next = NULL;
479     *p_tls = var;
480
481     vlc_mutex_lock (&super_mutex);
482     var->prev = vlc_threadvar_last;
483     vlc_threadvar_last = var;
484     vlc_mutex_unlock (&super_mutex);
485     return 0;
486 }
487
488 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
489 {
490     struct vlc_threadvar *var = *p_tls;
491
492     vlc_mutex_lock (&super_mutex);
493     if (var->prev != NULL)
494         var->prev->next = var->next;
495     else
496         vlc_threadvar_last = var->next;
497     if (var->next != NULL)
498         var->next->prev = var->prev;
499     vlc_mutex_unlock (&super_mutex);
500
501     TlsFree (var->id);
502     free (var);
503 }
504
505 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
506 {
507     return TlsSetValue (key->id, value) ? ENOMEM : 0;
508 }
509
510 void *vlc_threadvar_get (vlc_threadvar_t key)
511 {
512     return TlsGetValue (key->id);
513 }
514
515 /*** Threads ***/
516 void vlc_threads_setup (libvlc_int_t *p_libvlc)
517 {
518     (void) p_libvlc;
519 }
520
521 static void vlc_thread_cleanup (struct vlc_thread *th)
522 {
523     vlc_threadvar_t key;
524
525 retry:
526     /* TODO: use RW lock or something similar */
527     vlc_mutex_lock (&super_mutex);
528     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
529     {
530         void *value = vlc_threadvar_get (key);
531         if (value != NULL && key->destroy != NULL)
532         {
533             vlc_mutex_unlock (&super_mutex);
534             vlc_threadvar_set (key, NULL);
535             key->destroy (value);
536             goto retry;
537         }
538     }
539     vlc_mutex_unlock (&super_mutex);
540
541     if (th->detached)
542     {
543         CloseHandle (th->id);
544 #ifdef UNDER_CE
545         CloseHandle (th->cancel_event);
546 #endif
547         free (th);
548     }
549 }
550
551 static unsigned __stdcall vlc_entry (void *p)
552 {
553     struct vlc_thread *th = p;
554
555     vlc_threadvar_set (thread_key, th);
556     th->killable = true;
557     th->data = th->entry (th->data);
558     vlc_thread_cleanup (th);
559     return 0;
560 }
561
562 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
563                            void *(*entry) (void *), void *data, int priority)
564 {
565     struct vlc_thread *th = malloc (sizeof (*th));
566     if (unlikely(th == NULL))
567         return ENOMEM;
568     th->entry = entry;
569     th->data = data;
570     th->detached = detached;
571     th->killable = false; /* not until vlc_entry() ! */
572     th->killed = false;
573     th->cleaners = NULL;
574
575     HANDLE hThread;
576 #ifndef UNDER_CE
577     /* When using the MSVCRT C library you have to use the _beginthreadex
578      * function instead of CreateThread, otherwise you'll end up with
579      * memory leaks and the signal functions not working (see Microsoft
580      * Knowledge Base, article 104641) */
581     uintptr_t h;
582
583     h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
584     if (h == 0)
585     {
586         int err = errno;
587         free (th);
588         return err;
589     }
590     hThread = (HANDLE)h;
591
592 #else
593     th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
594     if (th->cancel_event == NULL)
595     {
596         free (th);
597         return ENOMEM;
598     }
599
600     /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
601      * Thread handles act up, too. */
602     hThread = CreateThread (NULL, 128*1024, vlc_entry, th,
603                             CREATE_SUSPENDED, NULL);
604     if (hThread == NULL)
605     {
606         CloseHandle (th->cancel_event);
607         free (th);
608         return ENOMEM;
609     }
610
611 #endif
612     /* Thread is suspended, so we can safely set th->id */
613     th->id = hThread;
614     if (p_handle != NULL)
615         *p_handle = th;
616
617     if (priority)
618         SetThreadPriority (hThread, priority);
619
620     ResumeThread (hThread);
621
622     return 0;
623 }
624
625 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
626                 void *data, int priority)
627 {
628     return vlc_clone_attr (p_handle, false, entry, data, priority);
629 }
630
631 void vlc_join (vlc_thread_t th, void **result)
632 {
633     do
634         vlc_testcancel ();
635     while (vlc_WaitForSingleObject (th->id, INFINITE)
636                                                         == WAIT_IO_COMPLETION);
637
638     if (result != NULL)
639         *result = th->data;
640     CloseHandle (th->id);
641 #ifdef UNDER_CE
642     CloseHandle (th->cancel_event);
643 #endif
644     free (th);
645 }
646
647 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
648                       void *data, int priority)
649 {
650     vlc_thread_t th;
651     if (p_handle == NULL)
652         p_handle = &th;
653
654     return vlc_clone_attr (p_handle, true, entry, data, priority);
655 }
656
657 int vlc_set_priority (vlc_thread_t th, int priority)
658 {
659     if (!SetThreadPriority (th->id, priority))
660         return VLC_EGENERIC;
661     return VLC_SUCCESS;
662 }
663
664 /*** Thread cancellation ***/
665
666 /* APC procedure for thread cancellation */
667 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
668 {
669     struct vlc_thread *th = (void *)self;
670
671     if (likely(th != NULL))
672         th->killed = true;
673 }
674
675 void vlc_cancel (vlc_thread_t th)
676 {
677 #ifndef UNDER_CE
678     QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
679 #else
680     SetEvent (th->cancel_event);
681 #endif
682 }
683
684 int vlc_savecancel (void)
685 {
686     struct vlc_thread *th = vlc_threadvar_get (thread_key);
687     if (th == NULL)
688         return false; /* Main thread - cannot be cancelled anyway */
689
690     int state = th->killable;
691     th->killable = false;
692     return state;
693 }
694
695 void vlc_restorecancel (int state)
696 {
697     struct vlc_thread *th = vlc_threadvar_get (thread_key);
698     assert (state == false || state == true);
699
700     if (th == NULL)
701         return; /* Main thread - cannot be cancelled anyway */
702
703     assert (!th->killable);
704     th->killable = state != 0;
705 }
706
707 void vlc_testcancel (void)
708 {
709     struct vlc_thread *th = vlc_threadvar_get (thread_key);
710     if (th == NULL)
711         return; /* Main thread - cannot be cancelled anyway */
712
713     if (th->killable && th->killed)
714     {
715         for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
716              p->proc (p->data);
717
718         th->data = NULL; /* TODO: special value? */
719         vlc_thread_cleanup (th);
720 #ifndef UNDER_CE
721         _endthreadex(0);
722 #else
723         ExitThread(0);
724 #endif
725     }
726 }
727
728 void vlc_control_cancel (int cmd, ...)
729 {
730     /* NOTE: This function only modifies thread-specific data, so there is no
731      * need to lock anything. */
732     va_list ap;
733
734     struct vlc_thread *th = vlc_threadvar_get (thread_key);
735     if (th == NULL)
736         return; /* Main thread - cannot be cancelled anyway */
737
738     va_start (ap, cmd);
739     switch (cmd)
740     {
741         case VLC_CLEANUP_PUSH:
742         {
743             /* cleaner is a pointer to the caller stack, no need to allocate
744              * and copy anything. As a nice side effect, this cannot fail. */
745             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
746             cleaner->next = th->cleaners;
747             th->cleaners = cleaner;
748             break;
749         }
750
751         case VLC_CLEANUP_POP:
752         {
753             th->cleaners = th->cleaners->next;
754             break;
755         }
756     }
757     va_end (ap);
758 }
759
760
761 /*** Clock ***/
762 mtime_t mdate (void)
763 {
764     /* We don't need the real date, just the value of a high precision timer */
765     LARGE_INTEGER counter, freq;
766     if (!QueryPerformanceCounter (&counter)
767      || !QueryPerformanceFrequency (&freq))
768         abort();
769
770     /* Convert to from (1/freq) to microsecond resolution */
771     /* We need to split the division to avoid 63-bits overflow */
772     lldiv_t d = lldiv (counter.QuadPart, freq.QuadPart);
773
774     return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
775 }
776
777 #undef mwait
778 void mwait (mtime_t deadline)
779 {
780     mtime_t delay;
781
782     vlc_testcancel();
783     while ((delay = (deadline - mdate())) > 0)
784     {
785         delay /= 1000;
786         if (unlikely(delay > 0x7fffffff))
787             delay = 0x7fffffff;
788         vlc_Sleep (delay);
789         vlc_testcancel();
790     }
791 }
792
793 #undef msleep
794 void msleep (mtime_t delay)
795 {
796     mwait (mdate () + delay);
797 }
798
799
800 /*** Timers ***/
801 struct vlc_timer
802 {
803 #ifndef UNDER_CE
804     HANDLE handle;
805 #else
806     unsigned id;
807     unsigned interval;
808 #endif
809     void (*func) (void *);
810     void *data;
811 };
812
813 #ifndef UNDER_CE
814 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
815 {
816     struct vlc_timer *timer = val;
817
818     assert (timeout);
819     timer->func (timer->data);
820 }
821 #else
822 static void CALLBACK vlc_timer_do (unsigned timer_id, unsigned msg,
823                                    DWORD_PTR user, DWORD_PTR unused1,
824                                    DWORD_PTR unused2)
825 {
826     struct vlc_timer *timer = (struct vlc_timer *) user;
827     assert (timer_id == timer->id);
828     (void) msg;
829     (void) unused1;
830     (void) unused2;
831
832     timer->func (timer->data);
833
834     if (timer->interval)
835     {
836         mtime_t interval = timer->interval * 1000;
837         vlc_timer_schedule (timer, false, interval, interval);
838     }
839 }
840 #endif
841
842 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
843 {
844     struct vlc_timer *timer = malloc (sizeof (*timer));
845
846     if (timer == NULL)
847         return ENOMEM;
848     timer->func = func;
849     timer->data = data;
850 #ifndef UNDER_CE
851     timer->handle = INVALID_HANDLE_VALUE;
852 #else
853     timer->id = 0;
854     timer->interval = 0;
855 #endif
856     *id = timer;
857     return 0;
858 }
859
860 void vlc_timer_destroy (vlc_timer_t timer)
861 {
862 #ifndef UNDER_CE
863     if (timer->handle != INVALID_HANDLE_VALUE)
864         DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
865 #else
866     if (timer->id)
867         timeKillEvent (timer->id);
868     /* FIXME: timers that have not yet completed will trigger use-after-free */
869 #endif
870     free (timer);
871 }
872
873 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
874                          mtime_t value, mtime_t interval)
875 {
876 #ifndef UNDER_CE
877     if (timer->handle != INVALID_HANDLE_VALUE)
878     {
879         DeleteTimerQueueTimer (NULL, timer->handle, NULL);
880         timer->handle = INVALID_HANDLE_VALUE;
881     }
882 #else
883     if (timer->id)
884     {
885         timeKillEvent (timer->id);
886         timer->id = 0;
887         timer->interval = 0;
888     }
889 #endif
890     if (value == 0)
891         return; /* Disarm */
892
893     if (absolute)
894         value -= mdate ();
895     value = (value + 999) / 1000;
896     interval = (interval + 999) / 1000;
897
898 #ifndef UNDER_CE
899     if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
900                                 value, interval, WT_EXECUTEDEFAULT))
901 #else
902     TIMECAPS caps;
903     timeGetDevCaps (&caps, sizeof(caps));
904
905     unsigned delay = value;
906     delay = __MAX(delay, caps.wPeriodMin);
907     delay = __MIN(delay, caps.wPeriodMax);
908
909     unsigned event = TIME_ONESHOT;
910
911     if (interval == delay)
912         event = TIME_PERIODIC;
913     else if (interval)
914         timer->interval = interval;
915
916     timer->id = timeSetEvent (delay, delay / 20, vlc_timer_do, (DWORD) timer,
917                               event);
918     if (!timer->id)
919 #endif
920         abort ();
921 }
922
923 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
924 {
925     (void)timer;
926     return 0;
927 }
928
929
930 /*** CPU ***/
931 unsigned vlc_GetCPUCount (void)
932 {
933 #ifndef UNDER_CE
934     DWORD process;
935     DWORD system;
936
937     if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
938         return popcount (system);
939 #endif
940      return 1;
941 }