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