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