cpu: privatize vlc_CPU_init()
[vlc.git] / src / win32 / thread.c
blob0a6c48dd9fb516c9840fd49fd1db0228181ee762
1 /*****************************************************************************
2 * thread.c : Win32 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2016 VLC authors and VideoLAN
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
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.
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.
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 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <vlc_common.h>
34 #include "libvlc.h"
35 #include <stdarg.h>
36 #include <stdatomic.h>
37 #include <assert.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <time.h>
41 #if !VLC_WINSTORE_APP
42 #include <mmsystem.h>
43 #endif
45 /*** Static mutex and condition variable ***/
46 static CRITICAL_SECTION super_mutex;
47 static CONDITION_VARIABLE super_variable;
49 #define IS_INTERRUPTIBLE (!VLC_WINSTORE_APP || _WIN32_WINNT >= 0x0A00)
51 /*** Threads ***/
52 static DWORD thread_key;
54 struct vlc_thread
56 HANDLE id;
58 bool killable;
59 atomic_bool killed;
60 vlc_cleanup_t *cleaners;
62 void *(*entry) (void *);
63 void *data;
65 struct
67 atomic_int *addr;
68 CRITICAL_SECTION lock;
69 } wait;
72 /*** Mutexes ***/
73 void vlc_mutex_init( vlc_mutex_t *p_mutex )
75 /* This creates a recursive mutex. This is OK as fast mutexes have
76 * no defined behavior in case of recursive locking. */
77 InitializeCriticalSection (&p_mutex->mutex);
78 p_mutex->dynamic = true;
81 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
83 InitializeCriticalSection( &p_mutex->mutex );
84 p_mutex->dynamic = true;
88 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
90 assert (p_mutex->dynamic);
91 DeleteCriticalSection (&p_mutex->mutex);
94 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
96 if (!p_mutex->dynamic)
97 { /* static mutexes */
98 EnterCriticalSection(&super_mutex);
99 while (p_mutex->locked)
101 p_mutex->contention++;
102 SleepConditionVariableCS(&super_variable, &super_mutex, INFINITE);
103 p_mutex->contention--;
105 p_mutex->locked = true;
106 LeaveCriticalSection(&super_mutex);
107 return;
110 EnterCriticalSection (&p_mutex->mutex);
113 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
115 if (!p_mutex->dynamic)
116 { /* static mutexes */
117 int ret = EBUSY;
119 EnterCriticalSection(&super_mutex);
120 if (!p_mutex->locked)
122 p_mutex->locked = true;
123 ret = 0;
125 LeaveCriticalSection(&super_mutex);
126 return ret;
129 return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
132 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
134 if (!p_mutex->dynamic)
135 { /* static mutexes */
136 EnterCriticalSection(&super_mutex);
137 assert (p_mutex->locked);
138 p_mutex->locked = false;
139 if (p_mutex->contention)
140 WakeAllConditionVariable(&super_variable);
141 LeaveCriticalSection(&super_mutex);
142 return;
145 LeaveCriticalSection (&p_mutex->mutex);
148 /*** Semaphore ***/
149 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
150 # include <stdalign.h>
152 static inline HANDLE *vlc_sem_handle_p(vlc_sem_t *sem)
154 /* NOTE: vlc_sem_t layout cannot easily depend on Windows version */
155 static_assert (sizeof (HANDLE) <= sizeof (vlc_sem_t), "Size mismatch!");
156 static_assert ((alignof (HANDLE) % alignof (vlc_sem_t)) == 0,
157 "Alignment mismatch");
158 return (HANDLE *)sem;
160 #define vlc_sem_handle(sem) (*vlc_sem_handle_p(sem))
162 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
164 HANDLE handle = CreateSemaphore(NULL, value, 0x7fffffff, NULL);
165 if (handle == NULL)
166 abort ();
168 vlc_sem_handle(sem) = handle;
171 void vlc_sem_destroy (vlc_sem_t *sem)
173 CloseHandle(vlc_sem_handle(sem));
176 int vlc_sem_post (vlc_sem_t *sem)
178 ReleaseSemaphore(vlc_sem_handle(sem), 1, NULL);
179 return 0; /* FIXME */
182 void vlc_sem_wait (vlc_sem_t *sem)
184 HANDLE handle = vlc_sem_handle(sem);
185 DWORD result;
189 vlc_testcancel ();
190 result = WaitForSingleObjectEx(handle, INFINITE, TRUE);
192 /* Semaphore abandoned would be a bug. */
193 assert(result != WAIT_ABANDONED_0);
195 while (result == WAIT_IO_COMPLETION || result == WAIT_FAILED);
197 #endif
199 /*** One-time initialization ***/
200 static BOOL CALLBACK vlc_once_callback(INIT_ONCE *once, void *parm, void **ctx)
202 void (*cb)(void) = parm;
204 cb();
205 (void) once;
206 (void) ctx;
207 return TRUE;
210 void vlc_once(vlc_once_t *once, void (*cb)(void))
212 InitOnceExecuteOnce(once, vlc_once_callback, cb, NULL);
215 /*** Thread-specific variables (TLS) ***/
216 struct vlc_threadvar
218 DWORD id;
219 void (*destroy) (void *);
220 struct vlc_threadvar *prev;
221 struct vlc_threadvar *next;
222 } *vlc_threadvar_last = NULL;
224 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
226 struct vlc_threadvar *var = malloc (sizeof (*var));
227 if (unlikely(var == NULL))
228 return errno;
230 var->id = TlsAlloc();
231 if (var->id == TLS_OUT_OF_INDEXES)
233 free (var);
234 return EAGAIN;
236 var->destroy = destr;
237 var->next = NULL;
238 *p_tls = var;
240 EnterCriticalSection(&super_mutex);
241 var->prev = vlc_threadvar_last;
242 if (var->prev)
243 var->prev->next = var;
245 vlc_threadvar_last = var;
246 LeaveCriticalSection(&super_mutex);
247 return 0;
250 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
252 struct vlc_threadvar *var = *p_tls;
254 EnterCriticalSection(&super_mutex);
255 if (var->prev != NULL)
256 var->prev->next = var->next;
258 if (var->next != NULL)
259 var->next->prev = var->prev;
260 else
261 vlc_threadvar_last = var->prev;
263 LeaveCriticalSection(&super_mutex);
265 TlsFree (var->id);
266 free (var);
269 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
271 int saved = GetLastError ();
273 if (!TlsSetValue(key->id, value))
274 return ENOMEM;
276 SetLastError(saved);
277 return 0;
280 void *vlc_threadvar_get (vlc_threadvar_t key)
282 int saved = GetLastError ();
283 void *value = TlsGetValue (key->id);
285 SetLastError(saved);
286 return value;
289 static void vlc_threadvars_cleanup(void)
291 vlc_threadvar_t key;
292 retry:
293 /* TODO: use RW lock or something similar */
294 EnterCriticalSection(&super_mutex);
295 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
297 void *value = vlc_threadvar_get(key);
298 if (value != NULL && key->destroy != NULL)
300 LeaveCriticalSection(&super_mutex);
301 vlc_threadvar_set(key, NULL);
302 key->destroy(value);
303 goto retry;
306 LeaveCriticalSection(&super_mutex);
309 /*** Futeces^WAddress waits ***/
310 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
311 static BOOL (WINAPI *WaitOnAddress_)(VOID volatile *, PVOID, SIZE_T, DWORD);
312 #define WaitOnAddress (*WaitOnAddress_)
313 static VOID (WINAPI *WakeByAddressAll_)(PVOID);
314 #define WakeByAddressAll (*WakeByAddressAll_)
315 static VOID (WINAPI *WakeByAddressSingle_)(PVOID);
316 #define WakeByAddressSingle (*WakeByAddressSingle_)
318 static struct wait_addr_bucket
320 CRITICAL_SECTION lock;
321 CONDITION_VARIABLE wait;
322 } wait_addr_buckets[32];
324 static struct wait_addr_bucket *wait_addr_get_bucket(void volatile *addr)
326 uintptr_t u = (uintptr_t)addr;
328 return wait_addr_buckets + ((u >> 3) % ARRAY_SIZE(wait_addr_buckets));
331 static void vlc_wait_addr_init(void)
333 for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
335 struct wait_addr_bucket *bucket = wait_addr_buckets + i;
337 InitializeCriticalSection(&bucket->lock);
338 InitializeConditionVariable(&bucket->wait);
342 static void vlc_wait_addr_deinit(void)
344 for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
346 struct wait_addr_bucket *bucket = wait_addr_buckets + i;
348 DeleteCriticalSection(&bucket->lock);
352 static BOOL WINAPI WaitOnAddressFallback(void volatile *addr, void *value,
353 SIZE_T size, DWORD ms)
355 struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
356 uint64_t futex, val = 0;
357 BOOL ret = 0;
359 EnterCriticalSection(&bucket->lock);
361 switch (size)
363 case 1:
364 futex = atomic_load_explicit((atomic_char *)addr,
365 memory_order_relaxed);
366 val = *(const char *)value;
367 break;
368 case 2:
369 futex = atomic_load_explicit((atomic_short *)addr,
370 memory_order_relaxed);
371 val = *(const short *)value;
372 break;
373 case 4:
374 futex = atomic_load_explicit((atomic_int *)addr,
375 memory_order_relaxed);
376 val = *(const int *)value;
377 break;
378 case 8:
379 futex = atomic_load_explicit((atomic_llong *)addr,
380 memory_order_relaxed);
381 val = *(const long long *)value;
382 break;
383 default:
384 vlc_assert_unreachable();
387 if (futex == val)
388 ret = SleepConditionVariableCS(&bucket->wait, &bucket->lock, ms);
390 LeaveCriticalSection(&bucket->lock);
391 return ret;
394 static void WINAPI WakeByAddressFallback(void *addr)
396 struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
398 /* Acquire the bucket critical section (only) to enforce proper sequencing.
399 * The critical section does not protect any actual memory object. */
400 EnterCriticalSection(&bucket->lock);
401 /* No other threads can hold the lock for this bucket while it is held
402 * here. Thus any other thread either:
403 * - is already sleeping in SleepConditionVariableCS(), and to be woken up
404 * by the following WakeAllConditionVariable(), or
405 * - has yet to retrieve the value at the wait address (with the
406 * 'switch (size)' block). */
407 LeaveCriticalSection(&bucket->lock);
408 /* At this point, other threads can retrieve the value at the wait address.
409 * But the value will have already been changed by our call site, thus
410 * (futex == val) will be false, and the threads will not go to sleep. */
412 /* Wake up any thread that was already sleeping. Since there are more than
413 * one wait address per bucket, all threads must be woken up :-/ */
414 WakeAllConditionVariable(&bucket->wait);
416 #endif
418 void vlc_addr_wait(void *addr, unsigned val)
420 WaitOnAddress(addr, &val, sizeof (val), -1);
423 bool vlc_addr_timedwait(void *addr, unsigned val, mtime_t delay)
425 delay = (delay + 999) / 1000;
427 if (delay > 0x7fffffff)
429 WaitOnAddress(addr, &val, sizeof (val), 0x7fffffff);
430 return true; /* woke up early, claim spurious wake-up */
433 return WaitOnAddress(addr, &val, sizeof (val), delay);
436 void vlc_addr_signal(void *addr)
438 WakeByAddressSingle(addr);
441 void vlc_addr_broadcast(void *addr)
443 WakeByAddressAll(addr);
446 /*** Threads ***/
447 static void vlc_thread_destroy(vlc_thread_t th)
449 DeleteCriticalSection(&th->wait.lock);
450 free(th);
453 static unsigned __stdcall vlc_entry (void *p)
455 struct vlc_thread *th = p;
457 TlsSetValue(thread_key, th);
458 th->killable = true;
459 th->data = th->entry (th->data);
460 TlsSetValue(thread_key, NULL);
462 if (th->id == NULL) /* Detached thread */
463 vlc_thread_destroy(th);
464 return 0;
467 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
468 void *(*entry) (void *), void *data, int priority)
470 struct vlc_thread *th = malloc (sizeof (*th));
471 if (unlikely(th == NULL))
472 return ENOMEM;
473 th->entry = entry;
474 th->data = data;
475 th->killable = false; /* not until vlc_entry() ! */
476 atomic_init(&th->killed, false);
477 th->cleaners = NULL;
478 th->wait.addr = NULL;
479 InitializeCriticalSection(&th->wait.lock);
481 /* When using the MSVCRT C library you have to use the _beginthreadex
482 * function instead of CreateThread, otherwise you'll end up with
483 * memory leaks and the signal functions not working (see Microsoft
484 * Knowledge Base, article 104641) */
485 uintptr_t h = _beginthreadex (NULL, 0, vlc_entry, th, 0, NULL);
486 if (h == 0)
488 int err = errno;
489 free (th);
490 return err;
493 if (detached)
495 CloseHandle((HANDLE)h);
496 th->id = NULL;
498 else
499 th->id = (HANDLE)h;
501 if (p_handle != NULL)
502 *p_handle = th;
504 if (priority)
505 SetThreadPriority (th->id, priority);
507 return 0;
510 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
511 void *data, int priority)
513 return vlc_clone_attr (p_handle, false, entry, data, priority);
516 void vlc_join (vlc_thread_t th, void **result)
518 DWORD ret;
522 vlc_testcancel ();
523 ret = WaitForSingleObjectEx(th->id, INFINITE, TRUE);
524 assert(ret != WAIT_ABANDONED_0);
526 while (ret == WAIT_IO_COMPLETION || ret == WAIT_FAILED);
528 if (result != NULL)
529 *result = th->data;
530 CloseHandle (th->id);
531 vlc_thread_destroy(th);
534 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
535 void *data, int priority)
537 vlc_thread_t th;
538 if (p_handle == NULL)
539 p_handle = &th;
541 return vlc_clone_attr (p_handle, true, entry, data, priority);
544 vlc_thread_t vlc_thread_self (void)
546 return TlsGetValue(thread_key);
549 unsigned long vlc_thread_id (void)
551 return GetCurrentThreadId ();
554 int vlc_set_priority (vlc_thread_t th, int priority)
556 if (!SetThreadPriority (th->id, priority))
557 return VLC_EGENERIC;
558 return VLC_SUCCESS;
561 /*** Thread cancellation ***/
563 #if IS_INTERRUPTIBLE
564 /* APC procedure for thread cancellation */
565 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
567 (void) self;
569 #endif
571 void vlc_cancel (vlc_thread_t th)
573 atomic_store_explicit(&th->killed, true, memory_order_relaxed);
575 EnterCriticalSection(&th->wait.lock);
576 if (th->wait.addr != NULL)
578 atomic_fetch_or_explicit(th->wait.addr, 1, memory_order_relaxed);
579 vlc_addr_broadcast(th->wait.addr);
581 LeaveCriticalSection(&th->wait.lock);
583 #if IS_INTERRUPTIBLE
584 QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
585 #endif
588 int vlc_savecancel (void)
590 struct vlc_thread *th = vlc_thread_self();
591 if (th == NULL)
592 return false; /* Main thread - cannot be cancelled anyway */
594 int state = th->killable;
595 th->killable = false;
596 return state;
599 void vlc_restorecancel (int state)
601 struct vlc_thread *th = vlc_thread_self();
602 assert (state == false || state == true);
604 if (th == NULL)
605 return; /* Main thread - cannot be cancelled anyway */
607 assert (!th->killable);
608 th->killable = state != 0;
611 void vlc_testcancel (void)
613 struct vlc_thread *th = vlc_thread_self();
614 if (th == NULL)
615 return; /* Main thread - cannot be cancelled anyway */
616 if (!th->killable)
617 return;
618 if (!atomic_load_explicit(&th->killed, memory_order_relaxed))
619 return;
621 th->killable = true; /* Do not re-enter cancellation cleanup */
623 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
624 p->proc (p->data);
626 th->data = NULL; /* TODO: special value? */
627 if (th->id == NULL) /* Detached thread */
628 vlc_thread_destroy(th);
629 _endthreadex(0);
632 void vlc_control_cancel (int cmd, ...)
634 /* NOTE: This function only modifies thread-specific data, so there is no
635 * need to lock anything. */
636 va_list ap;
638 struct vlc_thread *th = vlc_thread_self();
639 if (th == NULL)
640 return; /* Main thread - cannot be cancelled anyway */
642 va_start (ap, cmd);
643 switch (cmd)
645 case VLC_CLEANUP_PUSH:
647 /* cleaner is a pointer to the caller stack, no need to allocate
648 * and copy anything. As a nice side effect, this cannot fail. */
649 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
650 cleaner->next = th->cleaners;
651 th->cleaners = cleaner;
652 break;
655 case VLC_CLEANUP_POP:
657 th->cleaners = th->cleaners->next;
658 break;
661 case VLC_CANCEL_ADDR_SET:
663 void *addr = va_arg(ap, void *);
665 EnterCriticalSection(&th->wait.lock);
666 assert(th->wait.addr == NULL);
667 th->wait.addr = addr;
668 LeaveCriticalSection(&th->wait.lock);
669 break;
672 case VLC_CANCEL_ADDR_CLEAR:
674 void *addr = va_arg(ap, void *);
676 EnterCriticalSection(&th->wait.lock);
677 assert(th->wait.addr == addr);
678 th->wait.addr = NULL;
679 LeaveCriticalSection(&th->wait.lock);
680 break;
683 va_end (ap);
686 /*** Clock ***/
687 static union
689 struct
691 LARGE_INTEGER freq;
692 } perf;
693 #if !VLC_WINSTORE_APP
694 struct
696 MMRESULT (WINAPI *timeGetDevCaps)(LPTIMECAPS ptc,UINT cbtc);
697 DWORD (WINAPI *timeGetTime)(void);
698 } multimedia;
699 #endif
700 } clk;
702 static mtime_t mdate_interrupt (void)
704 ULONGLONG ts;
705 BOOL ret;
707 ret = QueryUnbiasedInterruptTime (&ts);
708 if (unlikely(!ret))
709 abort ();
711 /* hundreds of nanoseconds */
712 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
713 return ts / (10000000 / CLOCK_FREQ);
716 static mtime_t mdate_tick (void)
718 ULONGLONG ts = GetTickCount64 ();
720 /* milliseconds */
721 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
722 return ts * (CLOCK_FREQ / 1000);
724 #if !VLC_WINSTORE_APP
725 static mtime_t mdate_multimedia (void)
727 DWORD ts = clk.multimedia.timeGetTime ();
729 /* milliseconds */
730 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
731 return ts * (CLOCK_FREQ / 1000);
733 #endif
735 static mtime_t mdate_perf (void)
737 /* We don't need the real date, just the value of a high precision timer */
738 LARGE_INTEGER counter;
739 if (!QueryPerformanceCounter (&counter))
740 abort ();
742 /* Convert to from (1/freq) to microsecond resolution */
743 /* We need to split the division to avoid 63-bits overflow */
744 lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
746 return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
749 static mtime_t mdate_wall (void)
751 FILETIME ts;
752 ULARGE_INTEGER s;
754 #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) && (!VLC_WINSTORE_APP || _WIN32_WINNT >= 0x0A00)
755 GetSystemTimePreciseAsFileTime (&ts);
756 #else
757 GetSystemTimeAsFileTime (&ts);
758 #endif
759 s.LowPart = ts.dwLowDateTime;
760 s.HighPart = ts.dwHighDateTime;
761 /* hundreds of nanoseconds */
762 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
763 return s.QuadPart / (10000000 / CLOCK_FREQ);
766 static mtime_t mdate_default(void)
768 vlc_threads_setup(NULL);
769 return mdate_perf();
772 static mtime_t (*mdate_selected) (void) = mdate_default;
774 mtime_t mdate (void)
776 return mdate_selected ();
779 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
780 void (mwait)(mtime_t deadline)
782 mtime_t delay;
784 vlc_testcancel();
785 while ((delay = (deadline - mdate())) > 0)
787 delay = (delay + 999) / 1000;
788 if (unlikely(delay > 0x7fffffff))
789 delay = 0x7fffffff;
791 SleepEx(delay, TRUE);
792 vlc_testcancel();
796 void (msleep)(mtime_t delay)
798 mwait (mdate () + delay);
800 #endif
802 static BOOL SelectClockSource(void *data)
804 vlc_object_t *obj = data;
806 #if VLC_WINSTORE_APP
807 const char *name = "perf";
808 #else
809 const char *name = "multimedia";
810 #endif
811 char *str = NULL;
812 if (obj != NULL)
813 str = var_InheritString(obj, "clock-source");
814 if (str != NULL)
815 name = str;
816 if (!strcmp (name, "interrupt"))
818 msg_Dbg (obj, "using interrupt time as clock source");
819 mdate_selected = mdate_interrupt;
821 else
822 if (!strcmp (name, "tick"))
824 msg_Dbg (obj, "using Windows time as clock source");
825 mdate_selected = mdate_tick;
827 #if !VLC_WINSTORE_APP
828 else
829 if (!strcmp (name, "multimedia"))
831 TIMECAPS caps;
832 MMRESULT (WINAPI * timeBeginPeriod)(UINT);
834 HMODULE hWinmm = LoadLibrary(TEXT("winmm.dll"));
835 if (!hWinmm)
836 goto perf;
838 clk.multimedia.timeGetDevCaps = (void*)GetProcAddress(hWinmm, "timeGetDevCaps");
839 clk.multimedia.timeGetTime = (void*)GetProcAddress(hWinmm, "timeGetTime");
840 if (!clk.multimedia.timeGetDevCaps || !clk.multimedia.timeGetTime)
841 goto perf;
843 msg_Dbg (obj, "using multimedia timers as clock source");
844 if (clk.multimedia.timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
845 goto perf;
846 msg_Dbg (obj, " min period: %u ms, max period: %u ms",
847 caps.wPeriodMin, caps.wPeriodMax);
848 mdate_selected = mdate_multimedia;
850 timeBeginPeriod = (void*)GetProcAddress(hWinmm, "timeBeginPeriod");
851 if (timeBeginPeriod != NULL)
852 timeBeginPeriod(5);
854 #endif
855 else
856 if (!strcmp (name, "perf"))
858 perf:
859 msg_Dbg (obj, "using performance counters as clock source");
860 if (!QueryPerformanceFrequency (&clk.perf.freq))
861 abort ();
862 msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
863 mdate_selected = mdate_perf;
865 else
866 if (!strcmp (name, "wall"))
868 msg_Dbg (obj, "using system time as clock source");
869 mdate_selected = mdate_wall;
871 else
873 msg_Err (obj, "invalid clock source \"%s\"", name);
874 abort ();
876 free (str);
877 return TRUE;
880 size_t EnumClockSource (vlc_object_t *obj, const char *var,
881 char ***vp, char ***np)
883 const size_t max = 6;
884 char **values = xmalloc (sizeof (*values) * max);
885 char **names = xmalloc (sizeof (*names) * max);
886 size_t n = 0;
888 values[n] = xstrdup ("");
889 names[n] = xstrdup (_("Auto"));
890 n++;
891 values[n] = xstrdup ("interrupt");
892 names[n] = xstrdup ("Interrupt time");
893 n++;
894 values[n] = xstrdup ("tick");
895 names[n] = xstrdup ("Windows time");
896 n++;
897 #if !VLC_WINSTORE_APP
898 values[n] = xstrdup ("multimedia");
899 names[n] = xstrdup ("Multimedia timers");
900 n++;
901 #endif
902 values[n] = xstrdup ("perf");
903 names[n] = xstrdup ("Performance counters");
904 n++;
905 values[n] = xstrdup ("wall");
906 names[n] = xstrdup ("System time (DANGEROUS!)");
907 n++;
909 *vp = values;
910 *np = names;
911 (void) obj; (void) var;
912 return n;
916 /*** CPU ***/
917 unsigned vlc_GetCPUCount (void)
919 SYSTEM_INFO systemInfo;
921 GetNativeSystemInfo(&systemInfo);
923 return systemInfo.dwNumberOfProcessors;
927 /*** Initialization ***/
928 static CRITICAL_SECTION setup_lock; /* FIXME: use INIT_ONCE */
930 void vlc_threads_setup(libvlc_int_t *vlc)
932 EnterCriticalSection(&setup_lock);
933 if (mdate_selected != mdate_default)
935 LeaveCriticalSection(&setup_lock);
936 return;
939 if (!SelectClockSource((vlc != NULL) ? VLC_OBJECT(vlc) : NULL))
940 abort();
941 assert(mdate_selected != mdate_default);
943 #if !VLC_WINSTORE_APP
944 /* Raise default priority of the current process */
945 #ifndef ABOVE_NORMAL_PRIORITY_CLASS
946 # define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
947 #endif
948 if (var_InheritBool(vlc, "high-priority"))
950 if (SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS)
951 || SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
952 msg_Dbg(vlc, "raised process priority");
953 else
954 msg_Dbg(vlc, "could not raise process priority");
956 #endif
957 LeaveCriticalSection(&setup_lock);
960 #define LOOKUP(s) (((s##_) = (void *)GetProcAddress(h, #s)) != NULL)
962 extern vlc_rwlock_t config_lock;
963 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
965 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
967 (void) hinstDll;
968 (void) lpvReserved;
970 switch (fdwReason)
972 case DLL_PROCESS_ATTACH:
974 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
975 HANDLE h = GetModuleHandle(TEXT("kernel32.dll"));
976 if (unlikely(h == NULL))
977 return FALSE;
979 if (!LOOKUP(WaitOnAddress)
980 || !LOOKUP(WakeByAddressAll) || !LOOKUP(WakeByAddressSingle))
982 vlc_wait_addr_init();
983 WaitOnAddress_ = WaitOnAddressFallback;
984 WakeByAddressAll_ = WakeByAddressFallback;
985 WakeByAddressSingle_ = WakeByAddressFallback;
987 #endif
988 thread_key = TlsAlloc();
989 if (unlikely(thread_key == TLS_OUT_OF_INDEXES))
990 return FALSE;
991 InitializeCriticalSection(&setup_lock);
992 InitializeCriticalSection(&super_mutex);
993 InitializeConditionVariable(&super_variable);
994 vlc_rwlock_init (&config_lock);
995 break;
998 case DLL_PROCESS_DETACH:
999 vlc_rwlock_destroy (&config_lock);
1000 DeleteCriticalSection(&super_mutex);
1001 DeleteCriticalSection(&setup_lock);
1002 TlsFree(thread_key);
1003 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
1004 if (WaitOnAddress_ == WaitOnAddressFallback)
1005 vlc_wait_addr_deinit();
1006 #endif
1007 break;
1009 case DLL_THREAD_DETACH:
1010 vlc_threadvars_cleanup();
1011 break;
1013 return TRUE;