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