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 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, CLOCK_MONOTONIC);
184 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
186 vlc_cond_init_common (p_condvar, 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 CLOCK_MONOTONIC:
249 case 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 /*** Read/write locks */
307 /* SRW (Slim Read Write) locks are available in Vista+ only */
308 void vlc_rwlock_init (vlc_rwlock_t *lock)
312 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
316 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
320 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
324 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
328 # include "misc/rwlock.h"
331 /*** Thread-specific variables (TLS) ***/
335 void (*destroy) (void *);
336 struct vlc_threadvar *prev;
337 struct vlc_threadvar *next;
338 } *vlc_threadvar_last = NULL;
340 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
342 struct vlc_threadvar *var = malloc (sizeof (*var));
343 if (unlikely(var == NULL))
346 var->id = TlsAlloc();
347 if (var->id == TLS_OUT_OF_INDEXES)
352 var->destroy = destr;
356 vlc_mutex_lock (&super_mutex);
357 var->prev = vlc_threadvar_last;
359 var->prev->next = var;
361 vlc_threadvar_last = var;
362 vlc_mutex_unlock (&super_mutex);
366 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
368 struct vlc_threadvar *var = *p_tls;
370 vlc_mutex_lock (&super_mutex);
371 if (var->prev != NULL)
372 var->prev->next = var->next;
374 if (var->next != NULL)
375 var->next->prev = var->prev;
377 vlc_threadvar_last = var->prev;
379 vlc_mutex_unlock (&super_mutex);
385 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
387 return TlsSetValue (key->id, value) ? ENOMEM : 0;
390 void *vlc_threadvar_get (vlc_threadvar_t key)
392 return TlsGetValue (key->id);
396 static vlc_threadvar_t thread_key;
398 /** Per-thread data */
406 vlc_cleanup_t *cleaners;
408 void *(*entry) (void *);
412 static void vlc_thread_cleanup (struct vlc_thread *th)
417 /* TODO: use RW lock or something similar */
418 vlc_mutex_lock (&super_mutex);
419 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
421 void *value = vlc_threadvar_get (key);
422 if (value != NULL && key->destroy != NULL)
424 vlc_mutex_unlock (&super_mutex);
425 vlc_threadvar_set (key, NULL);
426 key->destroy (value);
430 vlc_mutex_unlock (&super_mutex);
434 CloseHandle (th->id);
439 static unsigned __stdcall vlc_entry (void *p)
441 struct vlc_thread *th = p;
443 vlc_threadvar_set (thread_key, th);
445 th->data = th->entry (th->data);
446 vlc_thread_cleanup (th);
450 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
451 void *(*entry) (void *), void *data, int priority)
453 struct vlc_thread *th = malloc (sizeof (*th));
454 if (unlikely(th == NULL))
458 th->detached = detached;
459 th->killable = false; /* not until vlc_entry() ! */
464 /* When using the MSVCRT C library you have to use the _beginthreadex
465 * function instead of CreateThread, otherwise you'll end up with
466 * memory leaks and the signal functions not working (see Microsoft
467 * Knowledge Base, article 104641) */
470 h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
479 /* Thread is suspended, so we can safely set th->id */
481 if (p_handle != NULL)
485 SetThreadPriority (hThread, priority);
487 ResumeThread (hThread);
492 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
493 void *data, int priority)
495 return vlc_clone_attr (p_handle, false, entry, data, priority);
498 void vlc_join (vlc_thread_t th, void **result)
502 while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
506 CloseHandle (th->id);
510 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
511 void *data, int priority)
514 if (p_handle == NULL)
517 return vlc_clone_attr (p_handle, true, entry, data, priority);
520 int vlc_set_priority (vlc_thread_t th, int priority)
522 if (!SetThreadPriority (th->id, priority))
527 /*** Thread cancellation ***/
529 /* APC procedure for thread cancellation */
530 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
532 struct vlc_thread *th = (void *)self;
534 if (likely(th != NULL))
538 void vlc_cancel (vlc_thread_t th)
540 QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
543 int vlc_savecancel (void)
545 struct vlc_thread *th = vlc_threadvar_get (thread_key);
547 return false; /* Main thread - cannot be cancelled anyway */
549 int state = th->killable;
550 th->killable = false;
554 void vlc_restorecancel (int state)
556 struct vlc_thread *th = vlc_threadvar_get (thread_key);
557 assert (state == false || state == true);
560 return; /* Main thread - cannot be cancelled anyway */
562 assert (!th->killable);
563 th->killable = state != 0;
566 void vlc_testcancel (void)
568 struct vlc_thread *th = vlc_threadvar_get (thread_key);
570 return; /* Main thread - cannot be cancelled anyway */
572 if (th->killable && th->killed)
574 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
577 th->data = NULL; /* TODO: special value? */
578 vlc_thread_cleanup (th);
583 void vlc_control_cancel (int cmd, ...)
585 /* NOTE: This function only modifies thread-specific data, so there is no
586 * need to lock anything. */
589 struct vlc_thread *th = vlc_threadvar_get (thread_key);
591 return; /* Main thread - cannot be cancelled anyway */
596 case VLC_CLEANUP_PUSH:
598 /* cleaner is a pointer to the caller stack, no need to allocate
599 * and copy anything. As a nice side effect, this cannot fail. */
600 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
601 cleaner->next = th->cleaners;
602 th->cleaners = cleaner;
606 case VLC_CLEANUP_POP:
608 th->cleaners = th->cleaners->next;
616 static CRITICAL_SECTION clock_lock;
618 static mtime_t mdate_giveup (void)
623 static mtime_t (*mdate_selected) (void) = mdate_giveup;
627 return mdate_selected ();
634 #if (_WIN32_WINNT < 0x0601)
635 BOOL (*query) (PULONGLONG);
640 #if (_WIN32_WINNT < 0x0600)
641 ULONGLONG (*get) (void);
650 static mtime_t mdate_interrupt (void)
655 #if (_WIN32_WINNT >= 0x0601)
656 ret = QueryUnbiasedInterruptTime (&ts);
658 ret = clk.interrupt.query (&ts);
663 /* hundreds of nanoseconds */
664 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
665 return ts / (10000000 / CLOCK_FREQ);
668 static mtime_t mdate_tick (void)
670 #if (_WIN32_WINNT >= 0x0600)
671 ULONGLONG ts = GetTickCount64 ();
673 ULONGLONG ts = clk.tick.get ();
677 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
678 return ts * (CLOCK_FREQ / 1000);
680 #include <mmsystem.h>
681 static mtime_t mdate_multimedia (void)
683 DWORD ts = timeGetTime ();
686 static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
687 return ts * (CLOCK_FREQ / 1000);
690 static mtime_t mdate_perf (void)
692 /* We don't need the real date, just the value of a high precision timer */
693 LARGE_INTEGER counter;
694 if (!QueryPerformanceCounter (&counter))
697 /* Convert to from (1/freq) to microsecond resolution */
698 /* We need to split the division to avoid 63-bits overflow */
699 lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
701 return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
704 static mtime_t mdate_wall (void)
709 #if (_WIN32_WINNT >= 0x0602)
710 GetSystemTimePreciseAsFileTime (&ts);
712 GetSystemTimeAsFileTime (&ts);
714 s.LowPart = ts.dwLowDateTime;
715 s.HighPart = ts.dwHighDateTime;
716 /* hundreds of nanoseconds */
717 static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
718 return s.QuadPart / (10000000 / CLOCK_FREQ);
722 void mwait (mtime_t deadline)
727 while ((delay = (deadline - mdate())) > 0)
730 if (unlikely(delay > 0x7fffffff))
738 void msleep (mtime_t delay)
740 mwait (mdate () + delay);
743 static void SelectClockSource (vlc_object_t *obj)
745 EnterCriticalSection (&clock_lock);
746 if (mdate_selected != mdate_giveup)
748 LeaveCriticalSection (&clock_lock);
752 const char *name = "perf";
753 char *str = var_InheritString (obj, "clock-source");
756 if (!strcmp (name, "interrupt"))
758 msg_Dbg (obj, "using interrupt time as clock source");
759 #if (_WIN32_WINNT < 0x0601)
760 HANDLE h = GetModuleHandle (_T("kernel32.dll"));
761 if (unlikely(h == NULL))
763 clk.interrupt.query = (void *)GetProcAddress (h,
764 _T("QueryUnbiasedInterruptTime"));
765 if (unlikely(clk.interrupt.query == NULL))
768 mdate_selected = mdate_interrupt;
771 if (!strcmp (name, "tick"))
773 msg_Dbg (obj, "using Windows time as clock source");
774 #if (_WIN32_WINNT < 0x0601)
775 HANDLE h = GetModuleHandle (_T("kernel32.dll"));
776 if (unlikely(h == NULL))
778 clk.tick.get = (void *)GetProcAddress (h, _T("GetTickCount64"));
779 if (unlikely(clk.tick.get == NULL))
782 mdate_selected = mdate_tick;
785 if (!strcmp (name, "multimedia"))
789 msg_Dbg (obj, "using multimedia timers as clock source");
790 if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
792 msg_Dbg (obj, " min period: %u ms, max period: %u ms",
793 caps.wPeriodMin, caps.wPeriodMax);
794 mdate_selected = mdate_multimedia;
797 if (!strcmp (name, "perf"))
799 msg_Dbg (obj, "using performance counters as clock source");
800 if (!QueryPerformanceFrequency (&clk.perf.freq))
802 msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
803 mdate_selected = mdate_perf;
806 if (!strcmp (name, "wall"))
808 msg_Dbg (obj, "using system time as clock source");
809 mdate_selected = mdate_wall;
813 msg_Err (obj, "invalid clock source \"%s\"", name);
816 LeaveCriticalSection (&clock_lock);
820 #define xstrdup(str) (strdup(str) ?: (abort(), NULL))
822 size_t EnumClockSource (vlc_object_t *obj, char ***vp, char ***np)
824 const size_t max = 6;
825 char **values = xmalloc (sizeof (*values) * max);
826 char **names = xmalloc (sizeof (*names) * max);
829 #if (_WIN32_WINNT < 0x0601)
830 DWORD version = LOWORD(GetVersion());
831 version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
834 values[n] = xstrdup ("");
835 names[n] = xstrdup (_("Auto"));
837 #if (_WIN32_WINNT < 0x0601)
838 if (version >= 0x0601)
841 values[n] = xstrdup ("interrupt");
842 names[n] = xstrdup ("Interrupt time");
845 #if (_WIN32_WINNT < 0x0600)
846 if (version >= 0x0600)
849 values[n] = xstrdup ("tick");
850 names[n] = xstrdup ("Windows time");
853 values[n] = xstrdup ("multimedia");
854 names[n] = xstrdup ("Multimedia timers");
856 values[n] = xstrdup ("perf");
857 names[n] = xstrdup ("Performance counters");
859 values[n] = xstrdup ("wall");
860 names[n] = xstrdup ("System time (DANGEROUS!)");
874 void (*func) (void *);
878 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
880 struct vlc_timer *timer = val;
883 timer->func (timer->data);
886 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
888 struct vlc_timer *timer = malloc (sizeof (*timer));
894 timer->handle = INVALID_HANDLE_VALUE;
899 void vlc_timer_destroy (vlc_timer_t timer)
901 if (timer->handle != INVALID_HANDLE_VALUE)
902 DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
906 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
907 mtime_t value, mtime_t interval)
909 if (timer->handle != INVALID_HANDLE_VALUE)
911 DeleteTimerQueueTimer (NULL, timer->handle, NULL);
912 timer->handle = INVALID_HANDLE_VALUE;
919 value = (value + 999) / 1000;
920 interval = (interval + 999) / 1000;
922 if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
923 value, interval, WT_EXECUTEDEFAULT))
927 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
935 unsigned vlc_GetCPUCount (void)
940 if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
941 return popcount (system);
946 /*** Initialization ***/
947 void vlc_threads_setup (libvlc_int_t *p_libvlc)
949 SelectClockSource (VLC_OBJECT(p_libvlc));
952 extern vlc_rwlock_t config_lock, msg_lock;
953 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
955 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
962 case DLL_PROCESS_ATTACH:
963 InitializeCriticalSection (&clock_lock);
964 vlc_mutex_init (&super_mutex);
965 vlc_cond_init (&super_variable);
966 vlc_threadvar_create (&thread_key, NULL);
967 vlc_rwlock_init (&config_lock);
968 vlc_rwlock_init (&msg_lock);
972 case DLL_PROCESS_DETACH:
973 vlc_rwlock_destroy (&msg_lock);
974 vlc_rwlock_destroy (&config_lock);
975 vlc_threadvar_delete (&thread_key);
976 vlc_cond_destroy (&super_variable);
977 vlc_mutex_destroy (&super_mutex);
978 DeleteCriticalSection (&clock_lock);