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