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