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