threads: fix race in vlc_cond_wait()
[vlc.git] / src / win32 / thread.c
1 /*****************************************************************************
2  * thread.c : Win32 back-end for LibVLC
3  *****************************************************************************
4  * Copyright (C) 1999-2016 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     struct
63     {
64         atomic_int      *addr;
65         CRITICAL_SECTION lock;
66     } wait;
67 };
68
69 /*** Common helpers ***/
70 #if !IS_INTERRUPTIBLE
71 static bool isCancelled(void);
72 #endif
73
74 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
75                                          DWORD delay)
76 {
77     DWORD ret;
78     if (count == 0)
79     {
80 #if !IS_INTERRUPTIBLE
81         do {
82             DWORD new_delay = 50;
83             if (new_delay > delay)
84                 new_delay = delay;
85             ret = SleepEx (new_delay, TRUE);
86             if (delay != INFINITE)
87                 delay -= new_delay;
88             if (isCancelled())
89                 ret = WAIT_IO_COMPLETION;
90         } while (delay && ret == 0);
91 #else
92         ret = SleepEx (delay, TRUE);
93 #endif
94
95         if (ret == 0)
96             ret = WAIT_TIMEOUT;
97     }
98     else {
99 #if !IS_INTERRUPTIBLE
100         do {
101             DWORD new_delay = 50;
102             if (new_delay > delay)
103                 new_delay = delay;
104             ret = WaitForMultipleObjectsEx (count, handles, FALSE, new_delay, TRUE);
105             if (delay != INFINITE)
106                 delay -= new_delay;
107             if (isCancelled())
108                 ret = WAIT_IO_COMPLETION;
109         } while (delay && ret == WAIT_TIMEOUT);
110 #else
111         ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
112 #endif
113     }
114
115     /* We do not abandon objects... this would be a bug */
116     assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
117
118     if (unlikely(ret == WAIT_FAILED))
119         abort (); /* We are screwed! */
120     return ret;
121 }
122
123 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
124 {
125     return vlc_WaitForMultipleObjects (1, &handle, delay);
126 }
127
128 static DWORD vlc_Sleep (DWORD delay)
129 {
130     DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
131     return (ret != WAIT_TIMEOUT) ? ret : 0;
132 }
133
134
135 /*** Mutexes ***/
136 void vlc_mutex_init( vlc_mutex_t *p_mutex )
137 {
138     /* This creates a recursive mutex. This is OK as fast mutexes have
139      * no defined behavior in case of recursive locking. */
140     InitializeCriticalSection (&p_mutex->mutex);
141     p_mutex->dynamic = true;
142 }
143
144 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
145 {
146     InitializeCriticalSection( &p_mutex->mutex );
147     p_mutex->dynamic = true;
148 }
149
150
151 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
152 {
153     assert (p_mutex->dynamic);
154     DeleteCriticalSection (&p_mutex->mutex);
155 }
156
157 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
158 {
159     if (!p_mutex->dynamic)
160     {   /* static mutexes */
161         int canc = vlc_savecancel ();
162         assert (p_mutex != &super_mutex); /* this one cannot be static */
163
164         vlc_mutex_lock (&super_mutex);
165         while (p_mutex->locked)
166         {
167             p_mutex->contention++;
168             vlc_cond_wait (&super_variable, &super_mutex);
169             p_mutex->contention--;
170         }
171         p_mutex->locked = true;
172         vlc_mutex_unlock (&super_mutex);
173         vlc_restorecancel (canc);
174         return;
175     }
176
177     EnterCriticalSection (&p_mutex->mutex);
178 }
179
180 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
181 {
182     if (!p_mutex->dynamic)
183     {   /* static mutexes */
184         int ret = EBUSY;
185
186         assert (p_mutex != &super_mutex); /* this one cannot be static */
187         vlc_mutex_lock (&super_mutex);
188         if (!p_mutex->locked)
189         {
190             p_mutex->locked = true;
191             ret = 0;
192         }
193         vlc_mutex_unlock (&super_mutex);
194         return ret;
195     }
196
197     return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
198 }
199
200 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
201 {
202     if (!p_mutex->dynamic)
203     {   /* static mutexes */
204         assert (p_mutex != &super_mutex); /* this one cannot be static */
205
206         vlc_mutex_lock (&super_mutex);
207         assert (p_mutex->locked);
208         p_mutex->locked = false;
209         if (p_mutex->contention)
210             vlc_cond_broadcast (&super_variable);
211         vlc_mutex_unlock (&super_mutex);
212         return;
213     }
214
215     LeaveCriticalSection (&p_mutex->mutex);
216 }
217
218 /*** Semaphore ***/
219 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
220 {
221     *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
222     if (*sem == NULL)
223         abort ();
224 }
225
226 void vlc_sem_destroy (vlc_sem_t *sem)
227 {
228     CloseHandle (*sem);
229 }
230
231 int vlc_sem_post (vlc_sem_t *sem)
232 {
233     ReleaseSemaphore (*sem, 1, NULL);
234     return 0; /* FIXME */
235 }
236
237 void vlc_sem_wait (vlc_sem_t *sem)
238 {
239     DWORD result;
240
241     do
242     {
243         vlc_testcancel ();
244         result = vlc_WaitForSingleObject (*sem, INFINITE);
245     }
246     while (result == WAIT_IO_COMPLETION);
247 }
248
249 /*** Thread-specific variables (TLS) ***/
250 struct vlc_threadvar
251 {
252     DWORD                 id;
253     void                (*destroy) (void *);
254     struct vlc_threadvar *prev;
255     struct vlc_threadvar *next;
256 } *vlc_threadvar_last = NULL;
257
258 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
259 {
260     struct vlc_threadvar *var = malloc (sizeof (*var));
261     if (unlikely(var == NULL))
262         return errno;
263
264     var->id = TlsAlloc();
265     if (var->id == TLS_OUT_OF_INDEXES)
266     {
267         free (var);
268         return EAGAIN;
269     }
270     var->destroy = destr;
271     var->next = NULL;
272     *p_tls = var;
273
274     vlc_mutex_lock (&super_mutex);
275     var->prev = vlc_threadvar_last;
276     if (var->prev)
277         var->prev->next = var;
278
279     vlc_threadvar_last = var;
280     vlc_mutex_unlock (&super_mutex);
281     return 0;
282 }
283
284 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
285 {
286     struct vlc_threadvar *var = *p_tls;
287
288     vlc_mutex_lock (&super_mutex);
289     if (var->prev != NULL)
290         var->prev->next = var->next;
291
292     if (var->next != NULL)
293         var->next->prev = var->prev;
294     else
295         vlc_threadvar_last = var->prev;
296
297     vlc_mutex_unlock (&super_mutex);
298
299     TlsFree (var->id);
300     free (var);
301 }
302
303 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
304 {
305     int saved = GetLastError ();
306     int val = TlsSetValue (key->id, value) ? ENOMEM : 0;
307
308     if (val == 0)
309         SetLastError(saved);
310     return val;
311 }
312
313 void *vlc_threadvar_get (vlc_threadvar_t key)
314 {
315     int saved = GetLastError ();
316     void *value = TlsGetValue (key->id);
317
318     SetLastError(saved);
319     return value;
320 }
321
322 static void vlc_threadvars_cleanup(void)
323 {
324     vlc_threadvar_t key;
325 retry:
326     /* TODO: use RW lock or something similar */
327     vlc_mutex_lock(&super_mutex);
328     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
329     {
330         void *value = vlc_threadvar_get(key);
331         if (value != NULL && key->destroy != NULL)
332         {
333             vlc_mutex_unlock(&super_mutex);
334             vlc_threadvar_set(key, NULL);
335             key->destroy(value);
336             goto retry;
337         }
338     }
339     vlc_mutex_unlock(&super_mutex);
340 }
341
342 /*** Condition variables (low-level) ***/
343 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
344 static VOID (WINAPI *InitializeConditionVariable_)(PCONDITION_VARIABLE);
345 #define InitializeConditionVariable InitializeConditionVariable_
346 static BOOL (WINAPI *SleepConditionVariableCS_)(PCONDITION_VARIABLE,
347                                                 PCRITICAL_SECTION, DWORD);
348 #define SleepConditionVariableCS SleepConditionVariableCS_
349 static VOID (WINAPI *WakeAllConditionVariable_)(PCONDITION_VARIABLE);
350 #define WakeAllConditionVariable WakeAllConditionVariable_
351
352 static void WINAPI DummyConditionVariable(CONDITION_VARIABLE *cv)
353 {
354     (void) cv;
355 }
356
357 static BOOL WINAPI SleepConditionVariableFallback(CONDITION_VARIABLE *cv,
358                                                   CRITICAL_SECTION *cs,
359                                                   DWORD ms)
360 {
361     (void) cv;
362     LeaveCriticalSection(cs);
363     SleepEx(0, TRUE);
364     EnterCriticalSection(cs);
365     return ms != 0;
366 }
367 #endif
368
369 /*** Futeces^WAddress waits ***/
370 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
371 static BOOL (WINAPI *WaitOnAddress_)(VOID volatile *, PVOID, SIZE_T, DWORD);
372 #define WaitOnAddress (*WaitOnAddress_)
373 static VOID (WINAPI *WakeByAddressAll_)(PVOID);
374 #define WakeByAddressAll (*WakeByAddressAll_)
375 static VOID (WINAPI *WakeByAddressSingle_)(PVOID);
376 #define WakeByAddressSingle (*WakeByAddressSingle_)
377
378 static struct wait_addr_bucket
379 {
380     CRITICAL_SECTION lock;
381     CONDITION_VARIABLE wait;
382 } wait_addr_buckets[32];
383
384 static struct wait_addr_bucket *wait_addr_get_bucket(void volatile *addr)
385 {
386     uintptr_t u = (uintptr_t)addr;
387
388     return wait_addr_buckets + ((u >> 3) % ARRAY_SIZE(wait_addr_buckets));
389 }
390
391 static void vlc_wait_addr_init(void)
392 {
393     for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
394     {
395         struct wait_addr_bucket *bucket = wait_addr_buckets + i;
396
397         InitializeCriticalSection(&bucket->lock);
398         InitializeConditionVariable(&bucket->wait);
399     }
400 }
401
402 static void vlc_wait_addr_deinit(void)
403 {
404     for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
405     {
406         struct wait_addr_bucket *bucket = wait_addr_buckets + i;
407
408         DeleteCriticalSection(&bucket->lock);
409     }
410 }
411
412 static BOOL WINAPI WaitOnAddressFallback(void volatile *addr, void *value,
413                                          SIZE_T size, DWORD ms)
414 {
415     struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
416     uint64_t futex, val = 0;
417     BOOL ret = 0;
418
419     EnterCriticalSection(&bucket->lock);
420
421     switch (size)
422     {
423         case 1:
424             futex = atomic_load_explicit((atomic_char *)addr,
425                                          memory_order_relaxed);
426             val = *(const char *)value;
427             break;
428         case 2:
429             futex = atomic_load_explicit((atomic_short *)addr,
430                                          memory_order_relaxed);
431             val = *(const short *)value;
432             break;
433         case 4:
434             futex = atomic_load_explicit((atomic_int *)addr,
435                                          memory_order_relaxed);
436             val = *(const int *)value;
437             break;
438         case 8:
439             futex = atomic_load_explicit((atomic_llong *)addr,
440                                          memory_order_relaxed);
441             val = *(const long long *)value;
442             break;
443         default:
444             vlc_assert_unreachable();
445     }
446
447     if (futex == val)
448         ret = SleepConditionVariableCS(&bucket->wait, &bucket->lock, ms);
449
450     LeaveCriticalSection(&bucket->lock);
451     return ret;
452 }
453
454 static void WINAPI WakeByAddressFallback(void *addr)
455 {
456     struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
457
458     /* Acquire the bucket critical section (only) to enforce proper sequencing.
459      * The critical section does not protect any actual memory object. */
460     EnterCriticalSection(&bucket->lock);
461     /* No other threads can hold the lock for this bucket while it is held
462      * here. Thus any other thread either:
463      * - is already sleeping in SleepConditionVariableCS(), and to be woken up
464      *   by the following WakeAllConditionVariable(), or
465      * - has yet to retrieve the value at the wait address (with the
466      *   'switch (size)' block). */
467     LeaveCriticalSection(&bucket->lock);
468     /* At this point, other threads can retrieve the value at the wait address.
469      * But the value will have already been changed by our call site, thus
470      * (futex == val) will be false, and the threads will not go to sleep. */
471
472     /* Wake up any thread that was already sleeping. Since there are more than
473      * one wait address per bucket, all threads must be woken up :-/ */
474     WakeAllConditionVariable(&bucket->wait);
475 }
476 #endif
477
478 void vlc_addr_wait(void *addr, unsigned val)
479 {
480     WaitOnAddress(addr, &val, sizeof (val), -1);
481 }
482
483 bool vlc_addr_timedwait(void *addr, unsigned val, mtime_t delay)
484 {
485     delay = (delay + 999) / 1000;
486
487     if (delay > 0x7fffffff)
488     {
489         WaitOnAddress(addr, &val, sizeof (val), 0x7fffffff);
490         return true; /* woke up early, claim spurious wake-up */
491     }
492
493     return WaitOnAddress(addr, &val, sizeof (val), delay);
494 }
495
496 void vlc_addr_signal(void *addr)
497 {
498     WakeByAddressSingle(addr);
499 }
500
501 void vlc_addr_broadcast(void *addr)
502 {
503     WakeByAddressAll(addr);
504 }
505
506 /*** Threads ***/
507 #if !IS_INTERRUPTIBLE
508 static bool isCancelled(void)
509 {
510     struct vlc_thread *th = vlc_thread_self();
511     if (th == NULL)
512         return false; /* Main thread - cannot be cancelled anyway */
513
514     return atomic_load(&th->killed);
515 }
516 #endif
517
518 static void vlc_thread_destroy(vlc_thread_t th)
519 {
520     DeleteCriticalSection(&th->wait.lock);
521     free(th);
522 }
523
524 static unsigned __stdcall vlc_entry (void *p)
525 {
526     struct vlc_thread *th = p;
527
528     TlsSetValue(thread_key, th);
529     th->killable = true;
530     th->data = th->entry (th->data);
531     TlsSetValue(thread_key, NULL);
532
533     if (th->id == NULL) /* Detached thread */
534         vlc_thread_destroy(th);
535     return 0;
536 }
537
538 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
539                            void *(*entry) (void *), void *data, int priority)
540 {
541     struct vlc_thread *th = malloc (sizeof (*th));
542     if (unlikely(th == NULL))
543         return ENOMEM;
544     th->entry = entry;
545     th->data = data;
546     th->killable = false; /* not until vlc_entry() ! */
547     atomic_init(&th->killed, false);
548     th->cleaners = NULL;
549     th->wait.addr = NULL;
550     InitializeCriticalSection(&th->wait.lock);
551
552     /* When using the MSVCRT C library you have to use the _beginthreadex
553      * function instead of CreateThread, otherwise you'll end up with
554      * memory leaks and the signal functions not working (see Microsoft
555      * Knowledge Base, article 104641) */
556     uintptr_t h = _beginthreadex (NULL, 0, vlc_entry, th, 0, NULL);
557     if (h == 0)
558     {
559         int err = errno;
560         free (th);
561         return err;
562     }
563
564     if (detached)
565     {
566         CloseHandle((HANDLE)h);
567         th->id = NULL;
568     }
569     else
570         th->id = (HANDLE)h;
571
572     if (p_handle != NULL)
573         *p_handle = th;
574
575     if (priority)
576         SetThreadPriority (th->id, priority);
577
578     return 0;
579 }
580
581 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
582                 void *data, int priority)
583 {
584     return vlc_clone_attr (p_handle, false, entry, data, priority);
585 }
586
587 void vlc_join (vlc_thread_t th, void **result)
588 {
589     do
590         vlc_testcancel ();
591     while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
592
593     if (result != NULL)
594         *result = th->data;
595     CloseHandle (th->id);
596     vlc_thread_destroy(th);
597 }
598
599 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
600                       void *data, int priority)
601 {
602     vlc_thread_t th;
603     if (p_handle == NULL)
604         p_handle = &th;
605
606     return vlc_clone_attr (p_handle, true, entry, data, priority);
607 }
608
609 vlc_thread_t vlc_thread_self (void)
610 {
611     return TlsGetValue(thread_key);
612 }
613
614 unsigned long vlc_thread_id (void)
615 {
616     return GetCurrentThreadId ();
617 }
618
619 int vlc_set_priority (vlc_thread_t th, int priority)
620 {
621     if (!SetThreadPriority (th->id, priority))
622         return VLC_EGENERIC;
623     return VLC_SUCCESS;
624 }
625
626 /*** Thread cancellation ***/
627
628 #if IS_INTERRUPTIBLE
629 /* APC procedure for thread cancellation */
630 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
631 {
632     (void) self;
633 }
634 #endif
635
636 void vlc_cancel (vlc_thread_t th)
637 {
638     atomic_store_explicit(&th->killed, true, memory_order_relaxed);
639
640     EnterCriticalSection(&th->wait.lock);
641     if (th->wait.addr != NULL)
642     {
643         atomic_fetch_or_explicit(th->wait.addr, 1, memory_order_relaxed);
644         vlc_addr_broadcast(th->wait.addr);
645     }
646     LeaveCriticalSection(&th->wait.lock);
647
648 #if IS_INTERRUPTIBLE
649     QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
650 #endif
651 }
652
653 int vlc_savecancel (void)
654 {
655     struct vlc_thread *th = vlc_thread_self();
656     if (th == NULL)
657         return false; /* Main thread - cannot be cancelled anyway */
658
659     int state = th->killable;
660     th->killable = false;
661     return state;
662 }
663
664 void vlc_restorecancel (int state)
665 {
666     struct vlc_thread *th = vlc_thread_self();
667     assert (state == false || state == true);
668
669     if (th == NULL)
670         return; /* Main thread - cannot be cancelled anyway */
671
672     assert (!th->killable);
673     th->killable = state != 0;
674 }
675
676 void vlc_testcancel (void)
677 {
678     struct vlc_thread *th = vlc_thread_self();
679     if (th == NULL)
680         return; /* Main thread - cannot be cancelled anyway */
681     if (!th->killable)
682         return;
683     if (!atomic_load_explicit(&th->killed, memory_order_relaxed))
684         return;
685
686     th->killable = true; /* Do not re-enter cancellation cleanup */
687
688     for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
689         p->proc (p->data);
690
691     th->data = NULL; /* TODO: special value? */
692     if (th->id == NULL) /* Detached thread */
693         vlc_thread_destroy(th);
694     _endthreadex(0);
695 }
696
697 void vlc_control_cancel (int cmd, ...)
698 {
699     /* NOTE: This function only modifies thread-specific data, so there is no
700      * need to lock anything. */
701     va_list ap;
702
703     struct vlc_thread *th = vlc_thread_self();
704     if (th == NULL)
705         return; /* Main thread - cannot be cancelled anyway */
706
707     va_start (ap, cmd);
708     switch (cmd)
709     {
710         case VLC_CLEANUP_PUSH:
711         {
712             /* cleaner is a pointer to the caller stack, no need to allocate
713              * and copy anything. As a nice side effect, this cannot fail. */
714             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
715             cleaner->next = th->cleaners;
716             th->cleaners = cleaner;
717             break;
718         }
719
720         case VLC_CLEANUP_POP:
721         {
722             th->cleaners = th->cleaners->next;
723             break;
724         }
725
726         case VLC_CANCEL_ADDR_SET:
727         {
728             void *addr = va_arg(ap, void *);
729
730             EnterCriticalSection(&th->wait.lock);
731             th->wait.addr = addr;
732             LeaveCriticalSection(&th->wait.lock);
733             break;
734         }
735
736         case VLC_CANCEL_ADDR_CLEAR:
737         {
738             void *addr = va_arg(ap, void *);
739
740             EnterCriticalSection(&th->wait.lock);
741             assert(th->wait.addr == addr);
742             th->wait.addr = NULL;
743             LeaveCriticalSection(&th->wait.lock);
744             break;
745         }
746     }
747     va_end (ap);
748 }
749
750 /*** Clock ***/
751 static union
752 {
753 #if (_WIN32_WINNT < 0x0601)
754     struct
755     {
756         BOOL (*query) (PULONGLONG);
757     } interrupt;
758 #endif
759 #if (_WIN32_WINNT < 0x0600)
760     struct
761     {
762         ULONGLONG (*get) (void);
763     } tick;
764 #endif
765     struct
766     {
767         LARGE_INTEGER freq;
768     } perf;
769 } clk;
770
771 static mtime_t mdate_interrupt (void)
772 {
773     ULONGLONG ts;
774     BOOL ret;
775
776 #if (_WIN32_WINNT >= 0x0601)
777     ret = QueryUnbiasedInterruptTime (&ts);
778 #else
779     ret = clk.interrupt.query (&ts);
780 #endif
781     if (unlikely(!ret))
782         abort ();
783
784     /* hundreds of nanoseconds */
785     static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
786     return ts / (10000000 / CLOCK_FREQ);
787 }
788
789 static mtime_t mdate_tick (void)
790 {
791 #if (_WIN32_WINNT >= 0x0600)
792     ULONGLONG ts = GetTickCount64 ();
793 #else
794     ULONGLONG ts = clk.tick.get ();
795 #endif
796
797     /* milliseconds */
798     static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
799     return ts * (CLOCK_FREQ / 1000);
800 }
801 #if !VLC_WINSTORE_APP
802 #include <mmsystem.h>
803 static mtime_t mdate_multimedia (void)
804 {
805      DWORD ts = timeGetTime ();
806
807     /* milliseconds */
808     static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
809     return ts * (CLOCK_FREQ / 1000);
810 }
811 #endif
812
813 static mtime_t mdate_perf (void)
814 {
815     /* We don't need the real date, just the value of a high precision timer */
816     LARGE_INTEGER counter;
817     if (!QueryPerformanceCounter (&counter))
818         abort ();
819
820     /* Convert to from (1/freq) to microsecond resolution */
821     /* We need to split the division to avoid 63-bits overflow */
822     lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
823
824     return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
825 }
826
827 static mtime_t mdate_wall (void)
828 {
829     FILETIME ts;
830     ULARGE_INTEGER s;
831
832 #if (_WIN32_WINNT >= 0x0602) && !VLC_WINSTORE_APP
833     GetSystemTimePreciseAsFileTime (&ts);
834 #else
835     GetSystemTimeAsFileTime (&ts);
836 #endif
837     s.LowPart = ts.dwLowDateTime;
838     s.HighPart = ts.dwHighDateTime;
839     /* hundreds of nanoseconds */
840     static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
841     return s.QuadPart / (10000000 / CLOCK_FREQ);
842 }
843
844 static CRITICAL_SECTION clock_lock;
845 static bool clock_used_early = false;
846
847 static mtime_t mdate_default(void)
848 {
849     EnterCriticalSection(&clock_lock);
850     if (!clock_used_early)
851     {
852         if (!QueryPerformanceFrequency(&clk.perf.freq))
853             abort();
854         clock_used_early = true;
855     }
856     LeaveCriticalSection(&clock_lock);
857
858     return mdate_perf();
859 }
860
861 static mtime_t (*mdate_selected) (void) = mdate_default;
862
863 mtime_t mdate (void)
864 {
865     return mdate_selected ();
866 }
867
868 #undef mwait
869 void mwait (mtime_t deadline)
870 {
871     mtime_t delay;
872
873     vlc_testcancel();
874     while ((delay = (deadline - mdate())) > 0)
875     {
876         delay = (delay + 999) / 1000;
877         if (unlikely(delay > 0x7fffffff))
878             delay = 0x7fffffff;
879         vlc_Sleep (delay);
880         vlc_testcancel();
881     }
882 }
883
884 #undef msleep
885 void msleep (mtime_t delay)
886 {
887     mwait (mdate () + delay);
888 }
889
890 static void SelectClockSource (vlc_object_t *obj)
891 {
892     EnterCriticalSection (&clock_lock);
893     if (mdate_selected != mdate_default)
894     {
895         LeaveCriticalSection (&clock_lock);
896         return;
897     }
898
899     assert(!clock_used_early);
900
901 #if VLC_WINSTORE_APP
902     const char *name = "perf";
903 #else
904     const char *name = "multimedia";
905 #endif
906     char *str = var_InheritString (obj, "clock-source");
907     if (str != NULL)
908         name = str;
909     if (!strcmp (name, "interrupt"))
910     {
911         msg_Dbg (obj, "using interrupt time as clock source");
912 #if (_WIN32_WINNT < 0x0601)
913         HANDLE h = GetModuleHandle (_T("kernel32.dll"));
914         if (unlikely(h == NULL))
915             abort ();
916         clk.interrupt.query = (void *)GetProcAddress (h,
917                                                       "QueryUnbiasedInterruptTime");
918         if (unlikely(clk.interrupt.query == NULL))
919             abort ();
920 #endif
921         mdate_selected = mdate_interrupt;
922     }
923     else
924     if (!strcmp (name, "tick"))
925     {
926         msg_Dbg (obj, "using Windows time as clock source");
927 #if (_WIN32_WINNT < 0x0600)
928         HANDLE h = GetModuleHandle (_T("kernel32.dll"));
929         if (unlikely(h == NULL))
930             abort ();
931         clk.tick.get = (void *)GetProcAddress (h, "GetTickCount64");
932         if (unlikely(clk.tick.get == NULL))
933             abort ();
934 #endif
935         mdate_selected = mdate_tick;
936     }
937 #if !VLC_WINSTORE_APP
938     else
939     if (!strcmp (name, "multimedia"))
940     {
941         TIMECAPS caps;
942
943         msg_Dbg (obj, "using multimedia timers as clock source");
944         if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
945             abort ();
946         msg_Dbg (obj, " min period: %u ms, max period: %u ms",
947                  caps.wPeriodMin, caps.wPeriodMax);
948         mdate_selected = mdate_multimedia;
949     }
950 #endif
951     else
952     if (!strcmp (name, "perf"))
953     {
954         msg_Dbg (obj, "using performance counters as clock source");
955         if (!QueryPerformanceFrequency (&clk.perf.freq))
956             abort ();
957         msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
958         mdate_selected = mdate_perf;
959     }
960     else
961     if (!strcmp (name, "wall"))
962     {
963         msg_Dbg (obj, "using system time as clock source");
964         mdate_selected = mdate_wall;
965     }
966     else
967     {
968         msg_Err (obj, "invalid clock source \"%s\"", name);
969         abort ();
970     }
971     LeaveCriticalSection (&clock_lock);
972     free (str);
973 }
974
975 size_t EnumClockSource (vlc_object_t *obj, const char *var,
976                         char ***vp, char ***np)
977 {
978     const size_t max = 6;
979     char **values = xmalloc (sizeof (*values) * max);
980     char **names = xmalloc (sizeof (*names) * max);
981     size_t n = 0;
982
983 #if (_WIN32_WINNT < 0x0601)
984     DWORD version = LOWORD(GetVersion());
985     version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
986 #endif
987
988     values[n] = xstrdup ("");
989     names[n] = xstrdup (_("Auto"));
990     n++;
991 #if (_WIN32_WINNT < 0x0601)
992     if (version >= 0x0601)
993 #endif
994     {
995         values[n] = xstrdup ("interrupt");
996         names[n] = xstrdup ("Interrupt time");
997         n++;
998     }
999 #if (_WIN32_WINNT < 0x0600)
1000     if (version >= 0x0600)
1001 #endif
1002     {
1003         values[n] = xstrdup ("tick");
1004         names[n] = xstrdup ("Windows time");
1005         n++;
1006     }
1007 #if !VLC_WINSTORE_APP
1008     values[n] = xstrdup ("multimedia");
1009     names[n] = xstrdup ("Multimedia timers");
1010     n++;
1011 #endif
1012     values[n] = xstrdup ("perf");
1013     names[n] = xstrdup ("Performance counters");
1014     n++;
1015     values[n] = xstrdup ("wall");
1016     names[n] = xstrdup ("System time (DANGEROUS!)");
1017     n++;
1018
1019     *vp = values;
1020     *np = names;
1021     (void) obj; (void) var;
1022     return n;
1023 }
1024
1025
1026 /*** Timers ***/
1027 struct vlc_timer
1028 {
1029     HANDLE handle;
1030     void (*func) (void *);
1031     void *data;
1032 };
1033
1034 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
1035 {
1036     struct vlc_timer *timer = val;
1037
1038     assert (timeout);
1039     timer->func (timer->data);
1040 }
1041
1042 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
1043 {
1044     struct vlc_timer *timer = malloc (sizeof (*timer));
1045
1046     if (timer == NULL)
1047         return ENOMEM;
1048     timer->func = func;
1049     timer->data = data;
1050     timer->handle = INVALID_HANDLE_VALUE;
1051     *id = timer;
1052     return 0;
1053 }
1054
1055 void vlc_timer_destroy (vlc_timer_t timer)
1056 {
1057 #if !VLC_WINSTORE_APP
1058     if (timer->handle != INVALID_HANDLE_VALUE)
1059         DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
1060 #endif
1061     free (timer);
1062 }
1063
1064 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
1065                          mtime_t value, mtime_t interval)
1066 {
1067     if (timer->handle != INVALID_HANDLE_VALUE)
1068     {
1069 #if !VLC_WINSTORE_APP
1070         DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
1071 #endif
1072         timer->handle = INVALID_HANDLE_VALUE;
1073     }
1074     if (value == 0)
1075         return; /* Disarm */
1076
1077     if (absolute)
1078     {
1079         value -= mdate ();
1080         if (value < 0)
1081             value = 0;
1082     }
1083     value = (value + 999) / 1000;
1084     interval = (interval + 999) / 1000;
1085
1086 #if !VLC_WINSTORE_APP
1087     if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
1088                                 value, interval, WT_EXECUTEDEFAULT))
1089 #endif
1090         abort ();
1091 }
1092
1093 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
1094 {
1095     (void)timer;
1096     return 0;
1097 }
1098
1099
1100 /*** CPU ***/
1101 unsigned vlc_GetCPUCount (void)
1102 {
1103     SYSTEM_INFO systemInfo;
1104
1105     GetNativeSystemInfo(&systemInfo);
1106
1107     return systemInfo.dwNumberOfProcessors;
1108 }
1109
1110
1111 /*** Initialization ***/
1112 void vlc_threads_setup (libvlc_int_t *p_libvlc)
1113 {
1114     SelectClockSource (VLC_OBJECT(p_libvlc));
1115 }
1116
1117 #define LOOKUP(s) (((s##_) = (void *)GetProcAddress(h, #s)) != NULL)
1118
1119 extern vlc_rwlock_t config_lock;
1120 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
1121
1122 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
1123 {
1124     (void) hinstDll;
1125     (void) lpvReserved;
1126
1127     switch (fdwReason)
1128     {
1129         case DLL_PROCESS_ATTACH:
1130         {
1131 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
1132             HANDLE h = GetModuleHandle(TEXT("kernel32.dll"));
1133             if (unlikely(h == NULL))
1134                 return FALSE;
1135
1136             if (!LOOKUP(WaitOnAddress)
1137              || !LOOKUP(WakeByAddressAll) || !LOOKUP(WakeByAddressSingle))
1138             {
1139 # if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
1140                 if (!LOOKUP(InitializeConditionVariable)
1141                  || !LOOKUP(SleepConditionVariableCS)
1142                  || !LOOKUP(WakeAllConditionVariable))
1143                 {
1144                     InitializeConditionVariable_ = DummyConditionVariable;
1145                     SleepConditionVariableCS_ = SleepConditionVariableFallback;
1146                     WakeAllConditionVariable_ = DummyConditionVariable;
1147                 }
1148 # endif
1149                 vlc_wait_addr_init();
1150                 WaitOnAddress_ = WaitOnAddressFallback;
1151                 WakeByAddressAll_ = WakeByAddressFallback;
1152                 WakeByAddressSingle_ = WakeByAddressFallback;
1153             }
1154 #endif
1155             thread_key = TlsAlloc();
1156             if (unlikely(thread_key == TLS_OUT_OF_INDEXES))
1157                 return FALSE;
1158             InitializeCriticalSection (&clock_lock);
1159             vlc_mutex_init (&super_mutex);
1160             vlc_cond_init (&super_variable);
1161             vlc_rwlock_init (&config_lock);
1162             vlc_CPU_init ();
1163             break;
1164         }
1165
1166         case DLL_PROCESS_DETACH:
1167             vlc_rwlock_destroy (&config_lock);
1168             vlc_cond_destroy (&super_variable);
1169             vlc_mutex_destroy (&super_mutex);
1170             DeleteCriticalSection (&clock_lock);
1171             TlsFree(thread_key);
1172 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
1173             if (WaitOnAddress_ == WaitOnAddressFallback)
1174                 vlc_wait_addr_deinit();
1175 #endif
1176             break;
1177
1178         case DLL_THREAD_DETACH:
1179             vlc_threadvars_cleanup();
1180             break;
1181     }
1182     return TRUE;
1183 }