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