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>
40 /*** Static mutex and condition variable ***/
41 static vlc_mutex_t super_mutex;
42 static vlc_cond_t super_variable;
45 /*** Common helpers ***/
46 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
52 ret = SleepEx (delay, TRUE);
57 ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
59 /* We do not abandon objects... this would be a bug */
60 assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
62 if (unlikely(ret == WAIT_FAILED))
63 abort (); /* We are screwed! */
67 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
69 return vlc_WaitForMultipleObjects (1, &handle, delay);
72 static DWORD vlc_Sleep (DWORD delay)
74 DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
75 return (ret != WAIT_TIMEOUT) ? ret : 0;
80 void vlc_mutex_init( vlc_mutex_t *p_mutex )
82 /* This creates a recursive mutex. This is OK as fast mutexes have
83 * no defined behavior in case of recursive locking. */
84 InitializeCriticalSection (&p_mutex->mutex);
85 p_mutex->dynamic = true;
88 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
90 InitializeCriticalSection( &p_mutex->mutex );
91 p_mutex->dynamic = true;
95 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
97 assert (p_mutex->dynamic);
98 DeleteCriticalSection (&p_mutex->mutex);
101 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
103 if (!p_mutex->dynamic)
104 { /* static mutexes */
105 int canc = vlc_savecancel ();
106 assert (p_mutex != &super_mutex); /* this one cannot be static */
108 vlc_mutex_lock (&super_mutex);
109 while (p_mutex->locked)
111 p_mutex->contention++;
112 vlc_cond_wait (&super_variable, &super_mutex);
113 p_mutex->contention--;
115 p_mutex->locked = true;
116 vlc_mutex_unlock (&super_mutex);
117 vlc_restorecancel (canc);
121 EnterCriticalSection (&p_mutex->mutex);
124 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
126 if (!p_mutex->dynamic)
127 { /* static mutexes */
130 assert (p_mutex != &super_mutex); /* this one cannot be static */
131 vlc_mutex_lock (&super_mutex);
132 if (!p_mutex->locked)
134 p_mutex->locked = true;
137 vlc_mutex_unlock (&super_mutex);
141 return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
144 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
146 if (!p_mutex->dynamic)
147 { /* static mutexes */
148 assert (p_mutex != &super_mutex); /* this one cannot be static */
150 vlc_mutex_lock (&super_mutex);
151 assert (p_mutex->locked);
152 p_mutex->locked = false;
153 if (p_mutex->contention)
154 vlc_cond_broadcast (&super_variable);
155 vlc_mutex_unlock (&super_mutex);
159 LeaveCriticalSection (&p_mutex->mutex);
162 /*** Condition variables ***/
165 VLC_CLOCK_STATIC=0, /* must be zero for VLC_STATIC_COND */
170 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
172 /* Create a manual-reset event (manual reset is needed for broadcast). */
173 p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
174 if (!p_condvar->handle)
176 p_condvar->clock = clock;
179 void vlc_cond_init (vlc_cond_t *p_condvar)
181 vlc_cond_init_common (p_condvar, VLC_CLOCK_MONOTONIC);
184 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
186 vlc_cond_init_common (p_condvar, VLC_CLOCK_REALTIME);
189 void vlc_cond_destroy (vlc_cond_t *p_condvar)
191 CloseHandle (p_condvar->handle);
194 void vlc_cond_signal (vlc_cond_t *p_condvar)
196 if (!p_condvar->clock)
199 /* This is suboptimal but works. */
200 vlc_cond_broadcast (p_condvar);
203 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
205 if (!p_condvar->clock)
208 /* Wake all threads up (as the event HANDLE has manual reset) */
209 SetEvent (p_condvar->handle);
212 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
216 if (!p_condvar->clock)
217 { /* FIXME FIXME FIXME */
225 vlc_mutex_unlock (p_mutex);
226 result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
227 vlc_mutex_lock (p_mutex);
229 while (result == WAIT_IO_COMPLETION);
231 ResetEvent (p_condvar->handle);
234 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
244 switch (p_condvar->clock)
246 case VLC_CLOCK_MONOTONIC:
249 case VLC_CLOCK_REALTIME: /* FIXME? sub-second precision */
250 total = CLOCK_FREQ * time (NULL);
253 assert (!p_condvar->clock);
254 /* FIXME FIXME FIXME */
258 total = (deadline - total) / 1000;
262 DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
263 vlc_mutex_unlock (p_mutex);
264 result = vlc_WaitForSingleObject (p_condvar->handle, delay);
265 vlc_mutex_lock (p_mutex);
267 while (result == WAIT_IO_COMPLETION);
269 ResetEvent (p_condvar->handle);
271 return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
275 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
277 *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
282 void vlc_sem_destroy (vlc_sem_t *sem)
287 int vlc_sem_post (vlc_sem_t *sem)
289 ReleaseSemaphore (*sem, 1, NULL);
290 return 0; /* FIXME */
293 void vlc_sem_wait (vlc_sem_t *sem)
300 result = vlc_WaitForSingleObject (*sem, INFINITE);
302 while (result == WAIT_IO_COMPLETION);
305 /*** Thread-specific variables (TLS) ***/
309 void (*destroy) (void *);
310 struct vlc_threadvar *prev;
311 struct vlc_threadvar *next;
312 } *vlc_threadvar_last = NULL;
314 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
316 struct vlc_threadvar *var = malloc (sizeof (*var));
317 if (unlikely(var == NULL))
320 var->id = TlsAlloc();
321 if (var->id == TLS_OUT_OF_INDEXES)
326 var->destroy = destr;
330 vlc_mutex_lock (&super_mutex);
331 var->prev = vlc_threadvar_last;
333 var->prev->next = var;
335 vlc_threadvar_last = var;
336 vlc_mutex_unlock (&super_mutex);
340 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
342 struct vlc_threadvar *var = *p_tls;
344 vlc_mutex_lock (&super_mutex);
345 if (var->prev != NULL)
346 var->prev->next = var->next;
348 if (var->next != NULL)
349 var->next->prev = var->prev;
351 vlc_threadvar_last = var->prev;
353 vlc_mutex_unlock (&super_mutex);
359 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
361 int saved = GetLastError ();
362 int val = TlsSetValue (key->id, value) ? ENOMEM : 0;
369 void *vlc_threadvar_get (vlc_threadvar_t key)
371 int saved = GetLastError ();
372 void *value = TlsGetValue (key->id);
379 static vlc_threadvar_t thread_key;
381 /** Per-thread data */
389 vlc_cleanup_t *cleaners;
391 void *(*entry) (void *);
395 static void vlc_thread_cleanup (struct vlc_thread *th)
400 /* TODO: use RW lock or something similar */
401 vlc_mutex_lock (&super_mutex);
402 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
404 void *value = vlc_threadvar_get (key);
405 if (value != NULL && key->destroy != NULL)
407 vlc_mutex_unlock (&super_mutex);
408 vlc_threadvar_set (key, NULL);
409 key->destroy (value);
413 vlc_mutex_unlock (&super_mutex);
417 CloseHandle (th->id);
422 static unsigned __stdcall vlc_entry (void *p)
424 struct vlc_thread *th = p;
426 vlc_threadvar_set (thread_key, th);
428 th->data = th->entry (th->data);
429 vlc_thread_cleanup (th);
433 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
434 void *(*entry) (void *), void *data, int priority)
436 struct vlc_thread *th = malloc (sizeof (*th));
437 if (unlikely(th == NULL))
441 th->detached = detached;
442 th->killable = false; /* not until vlc_entry() ! */
447 /* When using the MSVCRT C library you have to use the _beginthreadex
448 * function instead of CreateThread, otherwise you'll end up with
449 * memory leaks and the signal functions not working (see Microsoft
450 * Knowledge Base, article 104641) */
453 h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
462 /* Thread is suspended, so we can safely set th->id */
464 if (p_handle != NULL)
468 SetThreadPriority (hThread, priority);
470 ResumeThread (hThread);
475 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
476 void *data, int priority)
478 return vlc_clone_attr (p_handle, false, entry, data, priority);
481 void vlc_join (vlc_thread_t th, void **result)
485 while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
489 CloseHandle (th->id);
493 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
494 void *data, int priority)
497 if (p_handle == NULL)
500 return vlc_clone_attr (p_handle, true, entry, data, priority);
503 int vlc_set_priority (vlc_thread_t th, int priority)
505 if (!SetThreadPriority (th->id, priority))
510 /*** Thread cancellation ***/
512 /* APC procedure for thread cancellation */
513 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
515 struct vlc_thread *th = (void *)self;
517 if (likely(th != NULL))
521 void vlc_cancel (vlc_thread_t th)
523 QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
526 int vlc_savecancel (void)
528 struct vlc_thread *th = vlc_threadvar_get (thread_key);
530 return false; /* Main thread - cannot be cancelled anyway */
532 int state = th->killable;
533 th->killable = false;
537 void vlc_restorecancel (int state)
539 struct vlc_thread *th = vlc_threadvar_get (thread_key);
540 assert (state == false || state == true);
543 return; /* Main thread - cannot be cancelled anyway */
545 assert (!th->killable);
546 th->killable = state != 0;
549 void vlc_testcancel (void)
551 struct vlc_thread *th = vlc_threadvar_get (thread_key);
553 return; /* Main thread - cannot be cancelled anyway */
555 if (th->killable && th->killed)
557 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
560 th->data = NULL; /* TODO: special value? */
561 vlc_thread_cleanup (th);
566 void vlc_control_cancel (int cmd, ...)
568 /* NOTE: This function only modifies thread-specific data, so there is no
569 * need to lock anything. */
572 struct vlc_thread *th = vlc_threadvar_get (thread_key);
574 return; /* Main thread - cannot be cancelled anyway */
579 case VLC_CLEANUP_PUSH:
581 /* cleaner is a pointer to the caller stack, no need to allocate
582 * and copy anything. As a nice side effect, this cannot fail. */
583 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
584 cleaner->next = th->cleaners;
585 th->cleaners = cleaner;
589 case VLC_CLEANUP_POP:
591 th->cleaners = th->cleaners->next;
599 static CRITICAL_SECTION clock_lock;
601 static mtime_t mdate_giveup (void)
606 static mtime_t (*mdate_selected) (void) = mdate_giveup;
610 return mdate_selected ();
617 #if (_WIN32_WINNT < 0x0601)
618 BOOL (*query) (PULONGLONG);
623 #if (_WIN32_WINNT < 0x0600)
624 ULONGLONG (*get) (void);
633 static mtime_t mdate_interrupt (void)
638 #if (_WIN32_WINNT >= 0x0601)
639 ret = QueryUnbiasedInterruptTime (&ts);
641 ret = clk.interrupt.query (&ts);
646 /* hundreds of nanoseconds */
647 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
648 return ts / (10000000 / CLOCK_FREQ);
651 static mtime_t mdate_tick (void)
653 #if (_WIN32_WINNT >= 0x0600)
654 ULONGLONG ts = GetTickCount64 ();
656 ULONGLONG ts = clk.tick.get ();
660 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
661 return ts * (CLOCK_FREQ / 1000);
663 #if !VLC_WINSTORE_APP
664 #include <mmsystem.h>
665 static mtime_t mdate_multimedia (void)
667 DWORD ts = timeGetTime ();
670 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
671 return ts * (CLOCK_FREQ / 1000);
675 static mtime_t mdate_perf (void)
677 /* We don't need the real date, just the value of a high precision timer */
678 LARGE_INTEGER counter;
679 if (!QueryPerformanceCounter (&counter))
682 /* Convert to from (1/freq) to microsecond resolution */
683 /* We need to split the division to avoid 63-bits overflow */
684 lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
686 return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
689 static mtime_t mdate_wall (void)
694 #if (_WIN32_WINNT >= 0x0602) && !VLC_WINSTORE_APP
695 GetSystemTimePreciseAsFileTime (&ts);
697 GetSystemTimeAsFileTime (&ts);
699 s.LowPart = ts.dwLowDateTime;
700 s.HighPart = ts.dwHighDateTime;
701 /* hundreds of nanoseconds */
702 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
703 return s.QuadPart / (10000000 / CLOCK_FREQ);
707 void mwait (mtime_t deadline)
712 while ((delay = (deadline - mdate())) > 0)
715 if (unlikely(delay > 0x7fffffff))
723 void msleep (mtime_t delay)
725 mwait (mdate () + delay);
728 static void SelectClockSource (vlc_object_t *obj)
730 EnterCriticalSection (&clock_lock);
731 if (mdate_selected != mdate_giveup)
733 LeaveCriticalSection (&clock_lock);
738 const char *name = "perf";
740 const char *name = "multimedia";
742 char *str = var_InheritString (obj, "clock-source");
745 if (!strcmp (name, "interrupt"))
747 msg_Dbg (obj, "using interrupt time as clock source");
748 #if (_WIN32_WINNT < 0x0601)
749 HANDLE h = GetModuleHandle (_T("kernel32.dll"));
750 if (unlikely(h == NULL))
752 clk.interrupt.query = (void *)GetProcAddress (h,
753 "QueryUnbiasedInterruptTime");
754 if (unlikely(clk.interrupt.query == NULL))
757 mdate_selected = mdate_interrupt;
760 if (!strcmp (name, "tick"))
762 msg_Dbg (obj, "using Windows time as clock source");
763 #if (_WIN32_WINNT < 0x0600)
764 HANDLE h = GetModuleHandle (_T("kernel32.dll"));
765 if (unlikely(h == NULL))
767 clk.tick.get = (void *)GetProcAddress (h, "GetTickCount64");
768 if (unlikely(clk.tick.get == NULL))
771 mdate_selected = mdate_tick;
773 #if !VLC_WINSTORE_APP
775 if (!strcmp (name, "multimedia"))
779 msg_Dbg (obj, "using multimedia timers as clock source");
780 if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
782 msg_Dbg (obj, " min period: %u ms, max period: %u ms",
783 caps.wPeriodMin, caps.wPeriodMax);
784 mdate_selected = mdate_multimedia;
788 if (!strcmp (name, "perf"))
790 msg_Dbg (obj, "using performance counters as clock source");
791 if (!QueryPerformanceFrequency (&clk.perf.freq))
793 msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
794 mdate_selected = mdate_perf;
797 if (!strcmp (name, "wall"))
799 msg_Dbg (obj, "using system time as clock source");
800 mdate_selected = mdate_wall;
804 msg_Err (obj, "invalid clock source \"%s\"", name);
807 LeaveCriticalSection (&clock_lock);
811 #define xstrdup(str) (strdup(str) ?: (abort(), NULL))
813 size_t EnumClockSource (vlc_object_t *obj, const char *var,
814 char ***vp, char ***np)
816 const size_t max = 6;
817 char **values = xmalloc (sizeof (*values) * max);
818 char **names = xmalloc (sizeof (*names) * max);
821 #if (_WIN32_WINNT < 0x0601)
822 DWORD version = LOWORD(GetVersion());
823 version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
826 values[n] = xstrdup ("");
827 names[n] = xstrdup (_("Auto"));
829 #if (_WIN32_WINNT < 0x0601)
830 if (version >= 0x0601)
833 values[n] = xstrdup ("interrupt");
834 names[n] = xstrdup ("Interrupt time");
837 #if (_WIN32_WINNT < 0x0600)
838 if (version >= 0x0600)
841 values[n] = xstrdup ("tick");
842 names[n] = xstrdup ("Windows time");
845 #if !VLC_WINSTORE_APP
846 values[n] = xstrdup ("multimedia");
847 names[n] = xstrdup ("Multimedia timers");
850 values[n] = xstrdup ("perf");
851 names[n] = xstrdup ("Performance counters");
853 values[n] = xstrdup ("wall");
854 names[n] = xstrdup ("System time (DANGEROUS!)");
859 (void) obj; (void) var;
868 void (*func) (void *);
872 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
874 struct vlc_timer *timer = val;
877 timer->func (timer->data);
880 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
882 struct vlc_timer *timer = malloc (sizeof (*timer));
888 timer->handle = INVALID_HANDLE_VALUE;
893 void vlc_timer_destroy (vlc_timer_t timer)
895 if (timer->handle != INVALID_HANDLE_VALUE)
896 DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
900 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
901 mtime_t value, mtime_t interval)
903 if (timer->handle != INVALID_HANDLE_VALUE)
905 DeleteTimerQueueTimer (NULL, timer->handle, NULL);
906 timer->handle = INVALID_HANDLE_VALUE;
913 value = (value + 999) / 1000;
914 interval = (interval + 999) / 1000;
916 if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
917 value, interval, WT_EXECUTEDEFAULT))
921 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
929 unsigned vlc_GetCPUCount (void)
931 SYSTEM_INFO systemInfo;
933 GetNativeSystemInfo(&systemInfo);
935 return systemInfo.dwNumberOfProcessors;
939 /*** Initialization ***/
940 void vlc_threads_setup (libvlc_int_t *p_libvlc)
942 SelectClockSource (VLC_OBJECT(p_libvlc));
945 extern vlc_rwlock_t config_lock;
946 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
948 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
955 case DLL_PROCESS_ATTACH:
956 InitializeCriticalSection (&clock_lock);
957 vlc_mutex_init (&super_mutex);
958 vlc_cond_init (&super_variable);
959 vlc_threadvar_create (&thread_key, NULL);
960 vlc_rwlock_init (&config_lock);
964 case DLL_PROCESS_DETACH:
965 vlc_rwlock_destroy (&config_lock);
966 vlc_threadvar_delete (&thread_key);
967 vlc_cond_destroy (&super_variable);
968 vlc_mutex_destroy (&super_mutex);
969 DeleteCriticalSection (&clock_lock);