1 /*****************************************************************************
2 * thread.c : Win32 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2009 the VideoLAN team
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
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 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 General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
32 #include <vlc_common.h>
40 # include <mmsystem.h>
42 #include "config/configuration.h"
44 static vlc_threadvar_t thread_key;
59 vlc_cleanup_t *cleaners;
61 void *(*entry) (void *);
65 static CRITICAL_SECTION super_mutex;
66 static HANDLE super_cond;
68 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
70 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
77 case DLL_PROCESS_ATTACH:
78 super_cond = CreateEvent (NULL, TRUE, FALSE, NULL);
79 if (unlikely(!super_cond))
81 InitializeCriticalSection (&super_mutex);
82 vlc_threadvar_create (&thread_key, NULL);
83 vlc_rwlock_init (&config_lock);
86 case DLL_PROCESS_DETACH:
87 vlc_rwlock_destroy (&config_lock);
88 vlc_threadvar_delete (&thread_key);
89 DeleteCriticalSection (&super_mutex);
90 CloseHandle (super_cond);
96 static void CALLBACK vlc_cancel_self (ULONG_PTR);
98 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
103 HANDLE buf[count + 1];
105 struct vlc_thread *th = vlc_threadvar_get (thread_key);
108 memcpy (buf, handles, count * sizeof(HANDLE));
109 buf[count++] = th->cancel_event;
119 ret = WaitForMultipleObjects (count, handles, FALSE, delay);
121 if ((th != NULL) && (ret == WAIT_OBJECT_0 + count - 1))
123 vlc_cancel_self ((uintptr_t)th);
124 ret = WAIT_IO_COMPLETION;
129 ret = SleepEx (delay, TRUE);
134 ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
136 /* We do not abandon objects... this would be a bug */
137 assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
139 if (unlikely(ret == WAIT_FAILED))
140 abort (); /* We are screwed! */
144 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
146 return vlc_WaitForMultipleObjects (1, &handle, delay);
149 static DWORD vlc_Sleep (DWORD delay)
151 DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
152 return (ret != WAIT_TIMEOUT) ? ret : 0;
157 void vlc_mutex_init( vlc_mutex_t *p_mutex )
159 /* This creates a recursive mutex. This is OK as fast mutexes have
160 * no defined behavior in case of recursive locking. */
161 InitializeCriticalSection (&p_mutex->mutex);
162 p_mutex->dynamic = true;
165 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
167 InitializeCriticalSection( &p_mutex->mutex );
168 p_mutex->dynamic = true;
172 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
174 assert (p_mutex->dynamic);
175 DeleteCriticalSection (&p_mutex->mutex);
178 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
180 if (!p_mutex->dynamic)
181 { /* static mutexes (inefficient on Windows) */
182 EnterCriticalSection (&super_mutex);
183 while (p_mutex->locked)
185 p_mutex->contention++;
186 LeaveCriticalSection (&super_mutex);
187 WaitForSingleObject (super_cond, INFINITE);
188 EnterCriticalSection (&super_mutex);
189 assert (p_mutex->contention > 0);
190 p_mutex->contention--;
192 p_mutex->locked = true;
193 LeaveCriticalSection (&super_mutex);
197 EnterCriticalSection (&p_mutex->mutex);
200 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
202 if (!p_mutex->dynamic)
203 { /* static mutexes */
206 EnterCriticalSection (&super_mutex);
207 if (!p_mutex->locked)
209 p_mutex->locked = true;
212 LeaveCriticalSection (&super_mutex);
216 return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
219 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
221 if (!p_mutex->dynamic)
222 { /* static mutexes */
223 EnterCriticalSection (&super_mutex);
224 assert (p_mutex->locked);
225 p_mutex->locked = false;
226 if (p_mutex->contention > 0)
227 SetEvent (super_cond);
228 LeaveCriticalSection (&super_mutex);
232 LeaveCriticalSection (&p_mutex->mutex);
235 /*** Condition variables ***/
238 CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
242 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
244 /* Create a manual-reset event (manual reset is needed for broadcast). */
245 p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
246 if (!p_condvar->handle)
248 p_condvar->clock = clock;
251 void vlc_cond_init (vlc_cond_t *p_condvar)
253 vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
256 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
258 vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
261 void vlc_cond_destroy (vlc_cond_t *p_condvar)
263 CloseHandle (p_condvar->handle);
266 void vlc_cond_signal (vlc_cond_t *p_condvar)
268 if (!p_condvar->handle)
271 /* This is suboptimal but works. */
272 vlc_cond_broadcast (p_condvar);
275 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
277 if (!p_condvar->handle)
280 /* Wake all threads up (as the event HANDLE has manual reset) */
281 SetEvent (p_condvar->handle);
284 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
288 if (!p_condvar->handle)
289 { /* FIXME FIXME FIXME */
297 vlc_mutex_unlock (p_mutex);
298 result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
299 vlc_mutex_lock (p_mutex);
301 while (result == WAIT_IO_COMPLETION);
303 ResetEvent (p_condvar->handle);
306 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
311 if (!p_condvar->handle)
312 { /* FIXME FIXME FIXME */
322 switch (p_condvar->clock)
324 case CLOCK_REALTIME: /* FIXME? sub-second precision */
325 total = CLOCK_FREQ * time (NULL);
328 assert (p_condvar->clock == CLOCK_MONOTONIC);
332 total = (deadline - total) / 1000;
336 DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
337 vlc_mutex_unlock (p_mutex);
338 result = vlc_WaitForSingleObject (p_condvar->handle, delay);
339 vlc_mutex_lock (p_mutex);
341 while (result == WAIT_IO_COMPLETION);
343 ResetEvent (p_condvar->handle);
345 return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
349 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
351 *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
356 void vlc_sem_destroy (vlc_sem_t *sem)
361 int vlc_sem_post (vlc_sem_t *sem)
363 ReleaseSemaphore (*sem, 1, NULL);
364 return 0; /* FIXME */
367 void vlc_sem_wait (vlc_sem_t *sem)
374 result = vlc_WaitForSingleObject (*sem, INFINITE);
376 while (result == WAIT_IO_COMPLETION);
379 /*** Read/write locks */
380 /* SRW (Slim Read Write) locks are available in Vista+ only */
381 void vlc_rwlock_init (vlc_rwlock_t *lock)
383 vlc_mutex_init (&lock->mutex);
384 vlc_cond_init (&lock->read_wait);
385 vlc_cond_init (&lock->write_wait);
386 lock->readers = 0; /* active readers */
387 lock->writers = 0; /* waiting writers */
388 lock->writer = 0; /* ID of active writer */
391 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
393 vlc_cond_destroy (&lock->read_wait);
394 vlc_cond_destroy (&lock->write_wait);
395 vlc_mutex_destroy (&lock->mutex);
398 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
400 vlc_mutex_lock (&lock->mutex);
401 /* Recursive read-locking is allowed. With the infos available:
402 * - the loosest possible condition (no active writer) is:
403 * (lock->writer != 0)
404 * - the strictest possible condition is:
405 * (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0))
406 * or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0))
408 while (lock->writer != 0)
410 assert (lock->readers == 0);
411 vlc_cond_wait (&lock->read_wait, &lock->mutex);
413 if (unlikely(lock->readers == ULONG_MAX))
416 vlc_mutex_unlock (&lock->mutex);
419 static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock)
421 vlc_mutex_lock (&lock->mutex);
422 assert (lock->readers > 0);
424 /* If there are no readers left, wake up a writer. */
425 if (--lock->readers == 0 && lock->writers > 0)
426 vlc_cond_signal (&lock->write_wait);
427 vlc_mutex_unlock (&lock->mutex);
430 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
432 vlc_mutex_lock (&lock->mutex);
433 if (unlikely(lock->writers == ULONG_MAX))
436 /* Wait until nobody owns the lock in either way. */
437 while ((lock->readers > 0) || (lock->writer != 0))
438 vlc_cond_wait (&lock->write_wait, &lock->mutex);
440 assert (lock->writer == 0);
441 lock->writer = GetCurrentThreadId ();
442 vlc_mutex_unlock (&lock->mutex);
445 static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
447 vlc_mutex_lock (&lock->mutex);
448 assert (lock->writer == GetCurrentThreadId ());
449 assert (lock->readers == 0);
450 lock->writer = 0; /* Write unlock */
452 /* Let reader and writer compete. Scheduler decides who wins. */
453 if (lock->writers > 0)
454 vlc_cond_signal (&lock->write_wait);
455 vlc_cond_broadcast (&lock->read_wait);
456 vlc_mutex_unlock (&lock->mutex);
459 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
461 /* Note: If the lock is held for reading, lock->writer is nul.
462 * If the lock is held for writing, only this thread can store a value to
463 * lock->writer. Either way, lock->writer is safe to fetch here. */
464 if (lock->writer != 0)
465 vlc_rwlock_wrunlock (lock);
467 vlc_rwlock_rdunlock (lock);
470 /*** Thread-specific variables (TLS) ***/
474 void (*destroy) (void *);
475 struct vlc_threadvar *prev;
476 struct vlc_threadvar *next;
477 } *vlc_threadvar_last = NULL;
479 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
481 struct vlc_threadvar *var = malloc (sizeof (*var));
482 if (unlikely(var == NULL))
485 var->id = TlsAlloc();
486 if (var->id == TLS_OUT_OF_INDEXES)
491 var->destroy = destr;
495 EnterCriticalSection (&super_mutex);
496 var->prev = vlc_threadvar_last;
497 vlc_threadvar_last = var;
498 LeaveCriticalSection (&super_mutex);
502 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
504 struct vlc_threadvar *var = *p_tls;
506 EnterCriticalSection (&super_mutex);
507 if (var->prev != NULL)
508 var->prev->next = var->next;
510 vlc_threadvar_last = var->next;
511 if (var->next != NULL)
512 var->next->prev = var->prev;
513 LeaveCriticalSection (&super_mutex);
519 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
521 return TlsSetValue (key->id, value) ? ENOMEM : 0;
524 void *vlc_threadvar_get (vlc_threadvar_t key)
526 return TlsGetValue (key->id);
530 void vlc_threads_setup (libvlc_int_t *p_libvlc)
535 static void vlc_thread_cleanup (struct vlc_thread *th)
540 /* TODO: use RW lock or something similar */
541 EnterCriticalSection (&super_mutex);
542 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
544 void *value = vlc_threadvar_get (key);
545 if (value != NULL && key->destroy != NULL)
547 EnterCriticalSection (&super_mutex);
548 vlc_threadvar_set (key, NULL);
549 key->destroy (value);
553 EnterCriticalSection (&super_mutex);
557 CloseHandle (th->id);
559 CloseHandle (th->cancel_event);
565 static unsigned __stdcall vlc_entry (void *p)
567 struct vlc_thread *th = p;
569 vlc_threadvar_set (thread_key, th);
571 th->data = th->entry (th->data);
572 vlc_thread_cleanup (th);
576 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
577 void *(*entry) (void *), void *data, int priority)
579 struct vlc_thread *th = malloc (sizeof (*th));
580 if (unlikely(th == NULL))
584 th->detached = detached;
585 th->killable = false; /* not until vlc_entry() ! */
591 /* When using the MSVCRT C library you have to use the _beginthreadex
592 * function instead of CreateThread, otherwise you'll end up with
593 * memory leaks and the signal functions not working (see Microsoft
594 * Knowledge Base, article 104641) */
597 h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
607 th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
608 if (th->cancel_event == NULL)
614 /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
615 * Thread handles act up, too. */
616 hThread = CreateThread (NULL, 128*1024, vlc_entry, th,
617 CREATE_SUSPENDED, NULL);
620 CloseHandle (th->cancel_event);
626 /* Thread is suspended, so we can safely set th->id */
628 if (p_handle != NULL)
632 SetThreadPriority (hThread, priority);
634 ResumeThread (hThread);
639 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
640 void *data, int priority)
642 return vlc_clone_attr (p_handle, false, entry, data, priority);
645 void vlc_join (vlc_thread_t th, void **result)
649 while (vlc_WaitForSingleObject (th->id, INFINITE)
650 == WAIT_IO_COMPLETION);
654 CloseHandle (th->id);
656 CloseHandle (th->cancel_event);
661 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
662 void *data, int priority)
665 if (p_handle == NULL)
668 return vlc_clone_attr (p_handle, true, entry, data, priority);
671 int vlc_set_priority (vlc_thread_t th, int priority)
673 if (!SetThreadPriority (th->id, priority))
678 /*** Thread cancellation ***/
680 /* APC procedure for thread cancellation */
681 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
683 struct vlc_thread *th = (void *)self;
685 if (likely(th != NULL))
689 void vlc_cancel (vlc_thread_t th)
692 QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
694 SetEvent (th->cancel_event);
698 int vlc_savecancel (void)
700 struct vlc_thread *th = vlc_threadvar_get (thread_key);
702 return false; /* Main thread - cannot be cancelled anyway */
704 int state = th->killable;
705 th->killable = false;
709 void vlc_restorecancel (int state)
711 struct vlc_thread *th = vlc_threadvar_get (thread_key);
712 assert (state == false || state == true);
715 return; /* Main thread - cannot be cancelled anyway */
717 assert (!th->killable);
718 th->killable = state != 0;
721 void vlc_testcancel (void)
723 struct vlc_thread *th = vlc_threadvar_get (thread_key);
725 return; /* Main thread - cannot be cancelled anyway */
727 if (th->killable && th->killed)
729 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
732 th->data = NULL; /* TODO: special value? */
733 vlc_thread_cleanup (th);
742 void vlc_control_cancel (int cmd, ...)
744 /* NOTE: This function only modifies thread-specific data, so there is no
745 * need to lock anything. */
748 struct vlc_thread *th = vlc_threadvar_get (thread_key);
750 return; /* Main thread - cannot be cancelled anyway */
755 case VLC_CLEANUP_PUSH:
757 /* cleaner is a pointer to the caller stack, no need to allocate
758 * and copy anything. As a nice side effect, this cannot fail. */
759 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
760 cleaner->next = th->cleaners;
761 th->cleaners = cleaner;
765 case VLC_CLEANUP_POP:
767 th->cleaners = th->cleaners->next;
778 /* We don't need the real date, just the value of a high precision timer */
779 LARGE_INTEGER counter, freq;
780 if (!QueryPerformanceCounter (&counter)
781 || !QueryPerformanceFrequency (&freq))
784 /* Convert to from (1/freq) to microsecond resolution */
785 /* We need to split the division to avoid 63-bits overflow */
786 lldiv_t d = lldiv (counter.QuadPart, freq.QuadPart);
788 return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
792 void mwait (mtime_t deadline)
797 while ((delay = (deadline - mdate())) > 0)
800 if (unlikely(delay > 0x7fffffff))
808 void msleep (mtime_t delay)
810 mwait (mdate () + delay);
823 void (*func) (void *);
828 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
830 struct vlc_timer *timer = val;
833 timer->func (timer->data);
836 static void CALLBACK vlc_timer_do (unsigned timer_id, unsigned msg,
837 DWORD_PTR user, DWORD_PTR unused1,
840 struct vlc_timer *timer = (struct vlc_timer *) user;
841 assert (timer_id == timer->id);
846 timer->func (timer->data);
850 mtime_t interval = timer->interval * 1000;
851 vlc_timer_schedule (timer, false, interval, interval);
856 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
858 struct vlc_timer *timer = malloc (sizeof (*timer));
865 timer->handle = INVALID_HANDLE_VALUE;
874 void vlc_timer_destroy (vlc_timer_t timer)
877 if (timer->handle != INVALID_HANDLE_VALUE)
878 DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
881 timeKillEvent (timer->id);
882 /* FIXME: timers that have not yet completed will trigger use-after-free */
887 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
888 mtime_t value, mtime_t interval)
891 if (timer->handle != INVALID_HANDLE_VALUE)
893 DeleteTimerQueueTimer (NULL, timer->handle, NULL);
894 timer->handle = INVALID_HANDLE_VALUE;
899 timeKillEvent (timer->id);
909 value = (value + 999) / 1000;
910 interval = (interval + 999) / 1000;
913 if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
914 value, interval, WT_EXECUTEDEFAULT))
917 timeGetDevCaps (&caps, sizeof(caps));
919 unsigned delay = value;
920 delay = __MAX(delay, caps.wPeriodMin);
921 delay = __MIN(delay, caps.wPeriodMax);
923 unsigned event = TIME_ONESHOT;
925 if (interval == delay)
926 event = TIME_PERIODIC;
928 timer->interval = interval;
930 timer->id = timeSetEvent (delay, delay / 20, vlc_timer_do, (DWORD) timer,
937 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
945 unsigned vlc_GetCPUCount (void)
951 if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
952 return popcount (system);