1 /*****************************************************************************
2 * thread.c : Win32 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2009 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>
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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_atomic.h>
42 /*** Static mutex and condition variable ***/
43 static vlc_mutex_t super_mutex;
44 static vlc_cond_t super_variable;
47 /*** Common helpers ***/
49 static bool isCancelled(void);
52 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
61 if (new_delay > delay)
63 ret = SleepEx (new_delay, TRUE);
64 if (delay != INFINITE)
67 ret = WAIT_IO_COMPLETION;
68 } while (delay && ret == 0);
70 ret = SleepEx (delay, TRUE);
80 if (new_delay > delay)
82 ret = WaitForMultipleObjectsEx (count, handles, FALSE, new_delay, TRUE);
83 if (delay != INFINITE)
86 ret = WAIT_IO_COMPLETION;
87 } while (delay && ret == WAIT_TIMEOUT);
89 ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
93 /* We do not abandon objects... this would be a bug */
94 assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
96 if (unlikely(ret == WAIT_FAILED))
97 abort (); /* We are screwed! */
101 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
103 return vlc_WaitForMultipleObjects (1, &handle, delay);
106 static DWORD vlc_Sleep (DWORD delay)
108 DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
109 return (ret != WAIT_TIMEOUT) ? ret : 0;
114 void vlc_mutex_init( vlc_mutex_t *p_mutex )
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;
122 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
124 InitializeCriticalSection( &p_mutex->mutex );
125 p_mutex->dynamic = true;
129 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
131 assert (p_mutex->dynamic);
132 DeleteCriticalSection (&p_mutex->mutex);
135 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
137 if (!p_mutex->dynamic)
138 { /* static mutexes */
139 int canc = vlc_savecancel ();
140 assert (p_mutex != &super_mutex); /* this one cannot be static */
142 vlc_mutex_lock (&super_mutex);
143 while (p_mutex->locked)
145 p_mutex->contention++;
146 vlc_cond_wait (&super_variable, &super_mutex);
147 p_mutex->contention--;
149 p_mutex->locked = true;
150 vlc_mutex_unlock (&super_mutex);
151 vlc_restorecancel (canc);
155 EnterCriticalSection (&p_mutex->mutex);
158 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
160 if (!p_mutex->dynamic)
161 { /* static mutexes */
164 assert (p_mutex != &super_mutex); /* this one cannot be static */
165 vlc_mutex_lock (&super_mutex);
166 if (!p_mutex->locked)
168 p_mutex->locked = true;
171 vlc_mutex_unlock (&super_mutex);
175 return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
178 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
180 if (!p_mutex->dynamic)
181 { /* static mutexes */
182 assert (p_mutex != &super_mutex); /* this one cannot be static */
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);
193 LeaveCriticalSection (&p_mutex->mutex);
196 /*** Condition variables ***/
199 VLC_CLOCK_STATIC=0, /* must be zero for VLC_STATIC_COND */
204 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
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)
210 p_condvar->clock = clock;
213 void vlc_cond_init (vlc_cond_t *p_condvar)
215 vlc_cond_init_common (p_condvar, VLC_CLOCK_MONOTONIC);
218 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
220 vlc_cond_init_common (p_condvar, VLC_CLOCK_REALTIME);
223 void vlc_cond_destroy (vlc_cond_t *p_condvar)
225 CloseHandle (p_condvar->handle);
228 void vlc_cond_signal (vlc_cond_t *p_condvar)
230 /* This is suboptimal but works. */
231 vlc_cond_broadcast (p_condvar);
234 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
236 if (!p_condvar->clock)
239 /* Wake all threads up (as the event HANDLE has manual reset) */
240 SetEvent (p_condvar->handle);
243 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
247 if (!p_condvar->clock)
248 { /* FIXME FIXME FIXME */
256 vlc_mutex_unlock (p_mutex);
257 result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
258 vlc_mutex_lock (p_mutex);
260 while (result == WAIT_IO_COMPLETION);
262 ResetEvent (p_condvar->handle);
265 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
275 switch (p_condvar->clock)
277 case VLC_CLOCK_MONOTONIC:
280 case VLC_CLOCK_REALTIME: /* FIXME? sub-second precision */
281 total = CLOCK_FREQ * time (NULL);
284 assert (!p_condvar->clock);
285 /* FIXME FIXME FIXME */
289 total = (deadline - total) / 1000;
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);
298 while (result == WAIT_IO_COMPLETION);
300 ResetEvent (p_condvar->handle);
302 return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
306 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
308 *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
313 void vlc_sem_destroy (vlc_sem_t *sem)
318 int vlc_sem_post (vlc_sem_t *sem)
320 ReleaseSemaphore (*sem, 1, NULL);
321 return 0; /* FIXME */
324 void vlc_sem_wait (vlc_sem_t *sem)
331 result = vlc_WaitForSingleObject (*sem, INFINITE);
333 while (result == WAIT_IO_COMPLETION);
336 /*** Thread-specific variables (TLS) ***/
340 void (*destroy) (void *);
341 struct vlc_threadvar *prev;
342 struct vlc_threadvar *next;
343 } *vlc_threadvar_last = NULL;
345 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
347 struct vlc_threadvar *var = malloc (sizeof (*var));
348 if (unlikely(var == NULL))
351 var->id = TlsAlloc();
352 if (var->id == TLS_OUT_OF_INDEXES)
357 var->destroy = destr;
361 vlc_mutex_lock (&super_mutex);
362 var->prev = vlc_threadvar_last;
364 var->prev->next = var;
366 vlc_threadvar_last = var;
367 vlc_mutex_unlock (&super_mutex);
371 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
373 struct vlc_threadvar *var = *p_tls;
375 vlc_mutex_lock (&super_mutex);
376 if (var->prev != NULL)
377 var->prev->next = var->next;
379 if (var->next != NULL)
380 var->next->prev = var->prev;
382 vlc_threadvar_last = var->prev;
384 vlc_mutex_unlock (&super_mutex);
390 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
392 int saved = GetLastError ();
393 int val = TlsSetValue (key->id, value) ? ENOMEM : 0;
400 void *vlc_threadvar_get (vlc_threadvar_t key)
402 int saved = GetLastError ();
403 void *value = TlsGetValue (key->id);
410 static vlc_threadvar_t thread_key;
412 /** Per-thread data */
418 #if !VLC_WINSTORE_APP
423 vlc_cleanup_t *cleaners;
425 void *(*entry) (void *);
430 static bool isCancelled(void)
432 struct vlc_thread *th = vlc_threadvar_get (thread_key);
434 return false; /* Main thread - cannot be cancelled anyway */
436 return atomic_load(&th->killed);
440 static void vlc_thread_cleanup (struct vlc_thread *th)
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)
449 void *value = vlc_threadvar_get (key);
450 if (value != NULL && key->destroy != NULL)
452 vlc_mutex_unlock (&super_mutex);
453 vlc_threadvar_set (key, NULL);
454 key->destroy (value);
458 vlc_mutex_unlock (&super_mutex);
460 if (th->id == NULL) /* Detached thread */
464 static unsigned __stdcall vlc_entry (void *p)
466 struct vlc_thread *th = p;
468 vlc_threadvar_set (thread_key, th);
470 th->data = th->entry (th->data);
471 vlc_thread_cleanup (th);
475 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
476 void *(*entry) (void *), void *data, int priority)
478 struct vlc_thread *th = malloc (sizeof (*th));
479 if (unlikely(th == NULL))
483 th->killable = false; /* not until vlc_entry() ! */
484 #if !VLC_WINSTORE_APP
487 atomic_init(&th->killed, false);
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);
505 CloseHandle((HANDLE)h);
511 if (p_handle != NULL)
515 SetThreadPriority (th->id, priority);
520 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
521 void *data, int priority)
523 return vlc_clone_attr (p_handle, false, entry, data, priority);
526 void vlc_join (vlc_thread_t th, void **result)
530 while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
534 CloseHandle (th->id);
538 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
539 void *data, int priority)
542 if (p_handle == NULL)
545 return vlc_clone_attr (p_handle, true, entry, data, priority);
548 int vlc_set_priority (vlc_thread_t th, int priority)
550 if (!SetThreadPriority (th->id, priority))
555 /*** Thread cancellation ***/
557 #if !VLC_WINSTORE_APP
558 /* APC procedure for thread cancellation */
559 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
561 struct vlc_thread *th = (void *)self;
563 if (likely(th != NULL))
568 void vlc_cancel (vlc_thread_t th)
570 #if !VLC_WINSTORE_APP
571 QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
573 atomic_store (&th->killed, true);
577 int vlc_savecancel (void)
579 struct vlc_thread *th = vlc_threadvar_get (thread_key);
581 return false; /* Main thread - cannot be cancelled anyway */
583 int state = th->killable;
584 th->killable = false;
588 void vlc_restorecancel (int state)
590 struct vlc_thread *th = vlc_threadvar_get (thread_key);
591 assert (state == false || state == true);
594 return; /* Main thread - cannot be cancelled anyway */
596 assert (!th->killable);
597 th->killable = state != 0;
600 void vlc_testcancel (void)
602 struct vlc_thread *th = vlc_threadvar_get (thread_key);
604 return; /* Main thread - cannot be cancelled anyway */
607 #if !VLC_WINSTORE_APP
608 if (likely(!th->killed))
611 if (!atomic_load(&th->killed))
615 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
618 th->data = NULL; /* TODO: special value? */
619 vlc_thread_cleanup (th);
623 void vlc_control_cancel (int cmd, ...)
625 /* NOTE: This function only modifies thread-specific data, so there is no
626 * need to lock anything. */
629 struct vlc_thread *th = vlc_threadvar_get (thread_key);
631 return; /* Main thread - cannot be cancelled anyway */
636 case VLC_CLEANUP_PUSH:
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;
646 case VLC_CLEANUP_POP:
648 th->cleaners = th->cleaners->next;
656 static CRITICAL_SECTION clock_lock;
658 static mtime_t mdate_giveup (void)
663 static mtime_t (*mdate_selected) (void) = mdate_giveup;
667 return mdate_selected ();
672 #if (_WIN32_WINNT < 0x0601)
675 BOOL (*query) (PULONGLONG);
678 #if (_WIN32_WINNT < 0x0600)
681 ULONGLONG (*get) (void);
690 static mtime_t mdate_interrupt (void)
695 #if (_WIN32_WINNT >= 0x0601)
696 ret = QueryUnbiasedInterruptTime (&ts);
698 ret = clk.interrupt.query (&ts);
703 /* hundreds of nanoseconds */
704 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
705 return ts / (10000000 / CLOCK_FREQ);
708 static mtime_t mdate_tick (void)
710 #if (_WIN32_WINNT >= 0x0600)
711 ULONGLONG ts = GetTickCount64 ();
713 ULONGLONG ts = clk.tick.get ();
717 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
718 return ts * (CLOCK_FREQ / 1000);
720 #if !VLC_WINSTORE_APP
721 #include <mmsystem.h>
722 static mtime_t mdate_multimedia (void)
724 DWORD ts = timeGetTime ();
727 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
728 return ts * (CLOCK_FREQ / 1000);
732 static mtime_t mdate_perf (void)
734 /* We don't need the real date, just the value of a high precision timer */
735 LARGE_INTEGER counter;
736 if (!QueryPerformanceCounter (&counter))
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);
743 return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
746 static mtime_t mdate_wall (void)
751 #if (_WIN32_WINNT >= 0x0602) && !VLC_WINSTORE_APP
752 GetSystemTimePreciseAsFileTime (&ts);
754 GetSystemTimeAsFileTime (&ts);
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);
764 void mwait (mtime_t deadline)
769 while ((delay = (deadline - mdate())) > 0)
772 if (unlikely(delay > 0x7fffffff))
780 void msleep (mtime_t delay)
782 mwait (mdate () + delay);
785 static void SelectClockSource (vlc_object_t *obj)
787 EnterCriticalSection (&clock_lock);
788 if (mdate_selected != mdate_giveup)
790 LeaveCriticalSection (&clock_lock);
795 const char *name = "perf";
797 const char *name = "multimedia";
799 char *str = var_InheritString (obj, "clock-source");
802 if (!strcmp (name, "interrupt"))
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))
809 clk.interrupt.query = (void *)GetProcAddress (h,
810 "QueryUnbiasedInterruptTime");
811 if (unlikely(clk.interrupt.query == NULL))
814 mdate_selected = mdate_interrupt;
817 if (!strcmp (name, "tick"))
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))
824 clk.tick.get = (void *)GetProcAddress (h, "GetTickCount64");
825 if (unlikely(clk.tick.get == NULL))
828 mdate_selected = mdate_tick;
830 #if !VLC_WINSTORE_APP
832 if (!strcmp (name, "multimedia"))
836 msg_Dbg (obj, "using multimedia timers as clock source");
837 if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
839 msg_Dbg (obj, " min period: %u ms, max period: %u ms",
840 caps.wPeriodMin, caps.wPeriodMax);
841 mdate_selected = mdate_multimedia;
845 if (!strcmp (name, "perf"))
847 msg_Dbg (obj, "using performance counters as clock source");
848 if (!QueryPerformanceFrequency (&clk.perf.freq))
850 msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
851 mdate_selected = mdate_perf;
854 if (!strcmp (name, "wall"))
856 msg_Dbg (obj, "using system time as clock source");
857 mdate_selected = mdate_wall;
861 msg_Err (obj, "invalid clock source \"%s\"", name);
864 LeaveCriticalSection (&clock_lock);
868 size_t EnumClockSource (vlc_object_t *obj, const char *var,
869 char ***vp, char ***np)
871 const size_t max = 6;
872 char **values = xmalloc (sizeof (*values) * max);
873 char **names = xmalloc (sizeof (*names) * max);
876 #if (_WIN32_WINNT < 0x0601)
877 DWORD version = LOWORD(GetVersion());
878 version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
881 values[n] = xstrdup ("");
882 names[n] = xstrdup (_("Auto"));
884 #if (_WIN32_WINNT < 0x0601)
885 if (version >= 0x0601)
888 values[n] = xstrdup ("interrupt");
889 names[n] = xstrdup ("Interrupt time");
892 #if (_WIN32_WINNT < 0x0600)
893 if (version >= 0x0600)
896 values[n] = xstrdup ("tick");
897 names[n] = xstrdup ("Windows time");
900 #if !VLC_WINSTORE_APP
901 values[n] = xstrdup ("multimedia");
902 names[n] = xstrdup ("Multimedia timers");
905 values[n] = xstrdup ("perf");
906 names[n] = xstrdup ("Performance counters");
908 values[n] = xstrdup ("wall");
909 names[n] = xstrdup ("System time (DANGEROUS!)");
914 (void) obj; (void) var;
923 void (*func) (void *);
927 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
929 struct vlc_timer *timer = val;
932 timer->func (timer->data);
935 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
937 struct vlc_timer *timer = malloc (sizeof (*timer));
943 timer->handle = INVALID_HANDLE_VALUE;
948 void vlc_timer_destroy (vlc_timer_t timer)
950 if (timer->handle != INVALID_HANDLE_VALUE)
951 DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
955 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
956 mtime_t value, mtime_t interval)
958 if (timer->handle != INVALID_HANDLE_VALUE)
960 DeleteTimerQueueTimer (NULL, timer->handle, NULL);
961 timer->handle = INVALID_HANDLE_VALUE;
968 value = (value + 999) / 1000;
969 interval = (interval + 999) / 1000;
971 if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
972 value, interval, WT_EXECUTEDEFAULT))
976 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
984 unsigned vlc_GetCPUCount (void)
986 SYSTEM_INFO systemInfo;
988 GetNativeSystemInfo(&systemInfo);
990 return systemInfo.dwNumberOfProcessors;
994 /*** Initialization ***/
995 void vlc_threads_setup (libvlc_int_t *p_libvlc)
997 SelectClockSource (VLC_OBJECT(p_libvlc));
1000 extern vlc_rwlock_t config_lock;
1001 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
1003 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
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);
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);