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 # include <mmsystem.h>
43 static vlc_threadvar_t thread_key
;
58 vlc_cleanup_t
*cleaners
;
60 void *(*entry
) (void *);
64 static vlc_mutex_t super_mutex
;
65 static vlc_cond_t super_variable
;
66 extern vlc_rwlock_t config_lock
, msg_lock
;
68 BOOL WINAPI
DllMain (HINSTANCE
, DWORD
, LPVOID
);
70 BOOL WINAPI
DllMain (HINSTANCE hinstDll
, DWORD fdwReason
, LPVOID lpvReserved
)
77 case DLL_PROCESS_ATTACH
:
78 vlc_mutex_init (&super_mutex
);
79 vlc_cond_init (&super_variable
);
80 vlc_threadvar_create (&thread_key
, NULL
);
81 vlc_rwlock_init (&config_lock
);
82 vlc_rwlock_init (&msg_lock
);
86 case DLL_PROCESS_DETACH
:
87 vlc_rwlock_destroy (&msg_lock
);
88 vlc_rwlock_destroy (&config_lock
);
89 vlc_threadvar_delete (&thread_key
);
90 vlc_cond_destroy (&super_variable
);
91 vlc_mutex_destroy (&super_mutex
);
97 static void CALLBACK
vlc_cancel_self (ULONG_PTR
);
99 static DWORD
vlc_WaitForMultipleObjects (DWORD count
, const HANDLE
*handles
,
104 HANDLE buf
[count
+ 1];
106 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
109 memcpy (buf
, handles
, count
* sizeof(HANDLE
));
110 buf
[count
++] = th
->cancel_event
;
120 ret
= WaitForMultipleObjects (count
, handles
, FALSE
, delay
);
122 if ((th
!= NULL
) && (ret
== WAIT_OBJECT_0
+ count
- 1))
124 vlc_cancel_self ((uintptr_t)th
);
125 ret
= WAIT_IO_COMPLETION
;
130 ret
= SleepEx (delay
, TRUE
);
135 ret
= WaitForMultipleObjectsEx (count
, handles
, FALSE
, delay
, TRUE
);
137 /* We do not abandon objects... this would be a bug */
138 assert (ret
< WAIT_ABANDONED_0
|| WAIT_ABANDONED_0
+ count
- 1 < ret
);
140 if (unlikely(ret
== WAIT_FAILED
))
141 abort (); /* We are screwed! */
145 static DWORD
vlc_WaitForSingleObject (HANDLE handle
, DWORD delay
)
147 return vlc_WaitForMultipleObjects (1, &handle
, delay
);
150 static DWORD
vlc_Sleep (DWORD delay
)
152 DWORD ret
= vlc_WaitForMultipleObjects (0, NULL
, delay
);
153 return (ret
!= WAIT_TIMEOUT
) ? ret
: 0;
158 void vlc_mutex_init( vlc_mutex_t
*p_mutex
)
160 /* This creates a recursive mutex. This is OK as fast mutexes have
161 * no defined behavior in case of recursive locking. */
162 InitializeCriticalSection (&p_mutex
->mutex
);
163 p_mutex
->dynamic
= true;
166 void vlc_mutex_init_recursive( vlc_mutex_t
*p_mutex
)
168 InitializeCriticalSection( &p_mutex
->mutex
);
169 p_mutex
->dynamic
= true;
173 void vlc_mutex_destroy (vlc_mutex_t
*p_mutex
)
175 assert (p_mutex
->dynamic
);
176 DeleteCriticalSection (&p_mutex
->mutex
);
179 void vlc_mutex_lock (vlc_mutex_t
*p_mutex
)
181 if (!p_mutex
->dynamic
)
182 { /* static mutexes */
183 int canc
= vlc_savecancel ();
184 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
186 vlc_mutex_lock (&super_mutex
);
187 while (p_mutex
->locked
)
189 p_mutex
->contention
++;
190 vlc_cond_wait (&super_variable
, &super_mutex
);
191 p_mutex
->contention
--;
193 p_mutex
->locked
= true;
194 vlc_mutex_unlock (&super_mutex
);
195 vlc_restorecancel (canc
);
199 EnterCriticalSection (&p_mutex
->mutex
);
202 int vlc_mutex_trylock (vlc_mutex_t
*p_mutex
)
204 if (!p_mutex
->dynamic
)
205 { /* static mutexes */
208 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
209 vlc_mutex_lock (&super_mutex
);
210 if (!p_mutex
->locked
)
212 p_mutex
->locked
= true;
215 vlc_mutex_unlock (&super_mutex
);
219 return TryEnterCriticalSection (&p_mutex
->mutex
) ? 0 : EBUSY
;
222 void vlc_mutex_unlock (vlc_mutex_t
*p_mutex
)
224 if (!p_mutex
->dynamic
)
225 { /* static mutexes */
226 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
228 vlc_mutex_lock (&super_mutex
);
229 assert (p_mutex
->locked
);
230 p_mutex
->locked
= false;
231 if (p_mutex
->contention
)
232 vlc_cond_broadcast (&super_variable
);
233 vlc_mutex_unlock (&super_mutex
);
237 LeaveCriticalSection (&p_mutex
->mutex
);
240 /*** Condition variables ***/
243 CLOCK_REALTIME
=0, /* must be zero for VLC_STATIC_COND */
247 static void vlc_cond_init_common (vlc_cond_t
*p_condvar
, unsigned clock
)
249 /* Create a manual-reset event (manual reset is needed for broadcast). */
250 p_condvar
->handle
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
251 if (!p_condvar
->handle
)
253 p_condvar
->clock
= clock
;
256 void vlc_cond_init (vlc_cond_t
*p_condvar
)
258 vlc_cond_init_common (p_condvar
, CLOCK_MONOTONIC
);
261 void vlc_cond_init_daytime (vlc_cond_t
*p_condvar
)
263 vlc_cond_init_common (p_condvar
, CLOCK_REALTIME
);
266 void vlc_cond_destroy (vlc_cond_t
*p_condvar
)
268 CloseHandle (p_condvar
->handle
);
271 void vlc_cond_signal (vlc_cond_t
*p_condvar
)
273 if (!p_condvar
->handle
)
276 /* This is suboptimal but works. */
277 vlc_cond_broadcast (p_condvar
);
280 void vlc_cond_broadcast (vlc_cond_t
*p_condvar
)
282 if (!p_condvar
->handle
)
285 /* Wake all threads up (as the event HANDLE has manual reset) */
286 SetEvent (p_condvar
->handle
);
289 void vlc_cond_wait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
)
293 if (!p_condvar
->handle
)
294 { /* FIXME FIXME FIXME */
302 vlc_mutex_unlock (p_mutex
);
303 result
= vlc_WaitForSingleObject (p_condvar
->handle
, INFINITE
);
304 vlc_mutex_lock (p_mutex
);
306 while (result
== WAIT_IO_COMPLETION
);
308 ResetEvent (p_condvar
->handle
);
311 int vlc_cond_timedwait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
316 if (!p_condvar
->handle
)
317 { /* FIXME FIXME FIXME */
327 switch (p_condvar
->clock
)
329 case CLOCK_REALTIME
: /* FIXME? sub-second precision */
330 total
= CLOCK_FREQ
* time (NULL
);
333 assert (p_condvar
->clock
== CLOCK_MONOTONIC
);
337 total
= (deadline
- total
) / 1000;
341 DWORD delay
= (total
> 0x7fffffff) ? 0x7fffffff : total
;
342 vlc_mutex_unlock (p_mutex
);
343 result
= vlc_WaitForSingleObject (p_condvar
->handle
, delay
);
344 vlc_mutex_lock (p_mutex
);
346 while (result
== WAIT_IO_COMPLETION
);
348 ResetEvent (p_condvar
->handle
);
350 return (result
== WAIT_OBJECT_0
) ? 0 : ETIMEDOUT
;
354 void vlc_sem_init (vlc_sem_t
*sem
, unsigned value
)
356 *sem
= CreateSemaphore (NULL
, value
, 0x7fffffff, NULL
);
361 void vlc_sem_destroy (vlc_sem_t
*sem
)
366 int vlc_sem_post (vlc_sem_t
*sem
)
368 ReleaseSemaphore (*sem
, 1, NULL
);
369 return 0; /* FIXME */
372 void vlc_sem_wait (vlc_sem_t
*sem
)
379 result
= vlc_WaitForSingleObject (*sem
, INFINITE
);
381 while (result
== WAIT_IO_COMPLETION
);
384 /*** Read/write locks */
385 /* SRW (Slim Read Write) locks are available in Vista+ only */
386 void vlc_rwlock_init (vlc_rwlock_t
*lock
)
388 vlc_mutex_init (&lock
->mutex
);
389 vlc_cond_init (&lock
->wait
);
390 lock
->readers
= 0; /* active readers */
391 lock
->writers
= 0; /* waiting writers */
392 lock
->writer
= 0; /* ID of active writer */
395 void vlc_rwlock_destroy (vlc_rwlock_t
*lock
)
397 vlc_cond_destroy (&lock
->wait
);
398 vlc_mutex_destroy (&lock
->mutex
);
401 void vlc_rwlock_rdlock (vlc_rwlock_t
*lock
)
403 vlc_mutex_lock (&lock
->mutex
);
404 /* Recursive read-locking is allowed. With the infos available:
405 * - the loosest possible condition (no active writer) is:
406 * (lock->writer != 0)
407 * - the strictest possible condition is:
408 * (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0))
409 * or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0))
411 while (lock
->writer
!= 0)
413 assert (lock
->readers
== 0);
414 vlc_cond_wait (&lock
->wait
, &lock
->mutex
);
416 if (unlikely(lock
->readers
== ULONG_MAX
))
419 vlc_mutex_unlock (&lock
->mutex
);
422 static void vlc_rwlock_rdunlock (vlc_rwlock_t
*lock
)
424 vlc_mutex_lock (&lock
->mutex
);
425 assert (lock
->readers
> 0);
427 /* If there are no readers left, wake up a writer. */
428 if (--lock
->readers
== 0 && lock
->writers
> 0)
429 vlc_cond_signal (&lock
->wait
);
430 vlc_mutex_unlock (&lock
->mutex
);
433 void vlc_rwlock_wrlock (vlc_rwlock_t
*lock
)
435 vlc_mutex_lock (&lock
->mutex
);
436 if (unlikely(lock
->writers
== ULONG_MAX
))
439 /* Wait until nobody owns the lock in either way. */
440 while ((lock
->readers
> 0) || (lock
->writer
!= 0))
441 vlc_cond_wait (&lock
->wait
, &lock
->mutex
);
443 assert (lock
->writer
== 0);
444 lock
->writer
= GetCurrentThreadId ();
445 vlc_mutex_unlock (&lock
->mutex
);
448 static void vlc_rwlock_wrunlock (vlc_rwlock_t
*lock
)
450 vlc_mutex_lock (&lock
->mutex
);
451 assert (lock
->writer
== GetCurrentThreadId ());
452 assert (lock
->readers
== 0);
453 lock
->writer
= 0; /* Write unlock */
455 /* Let reader and writer compete. Scheduler decides who wins. */
456 vlc_cond_broadcast (&lock
->wait
);
457 vlc_mutex_unlock (&lock
->mutex
);
460 void vlc_rwlock_unlock (vlc_rwlock_t
*lock
)
462 /* Note: If the lock is held for reading, lock->writer is nul.
463 * If the lock is held for writing, only this thread can store a value to
464 * lock->writer. Either way, lock->writer is safe to fetch here. */
465 if (lock
->writer
!= 0)
466 vlc_rwlock_wrunlock (lock
);
468 vlc_rwlock_rdunlock (lock
);
471 /*** Thread-specific variables (TLS) ***/
475 void (*destroy
) (void *);
476 struct vlc_threadvar
*prev
;
477 struct vlc_threadvar
*next
;
478 } *vlc_threadvar_last
= NULL
;
480 int vlc_threadvar_create (vlc_threadvar_t
*p_tls
, void (*destr
) (void *))
482 struct vlc_threadvar
*var
= malloc (sizeof (*var
));
483 if (unlikely(var
== NULL
))
486 var
->id
= TlsAlloc();
487 if (var
->id
== TLS_OUT_OF_INDEXES
)
492 var
->destroy
= destr
;
496 vlc_mutex_lock (&super_mutex
);
497 var
->prev
= vlc_threadvar_last
;
499 var
->prev
->next
= var
;
501 vlc_threadvar_last
= var
;
502 vlc_mutex_unlock (&super_mutex
);
506 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
508 struct vlc_threadvar
*var
= *p_tls
;
510 vlc_mutex_lock (&super_mutex
);
511 if (var
->prev
!= NULL
)
512 var
->prev
->next
= var
->next
;
514 if (var
->next
!= NULL
)
515 var
->next
->prev
= var
->prev
;
517 vlc_threadvar_last
= var
->prev
;
519 vlc_mutex_unlock (&super_mutex
);
525 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
527 return TlsSetValue (key
->id
, value
) ? ENOMEM
: 0;
530 void *vlc_threadvar_get (vlc_threadvar_t key
)
532 return TlsGetValue (key
->id
);
536 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
541 static void vlc_thread_cleanup (struct vlc_thread
*th
)
546 /* TODO: use RW lock or something similar */
547 vlc_mutex_lock (&super_mutex
);
548 for (key
= vlc_threadvar_last
; key
!= NULL
; key
= key
->prev
)
550 void *value
= vlc_threadvar_get (key
);
551 if (value
!= NULL
&& key
->destroy
!= NULL
)
553 vlc_mutex_unlock (&super_mutex
);
554 vlc_threadvar_set (key
, NULL
);
555 key
->destroy (value
);
559 vlc_mutex_unlock (&super_mutex
);
563 CloseHandle (th
->id
);
565 CloseHandle (th
->cancel_event
);
571 static unsigned __stdcall
vlc_entry (void *p
)
573 struct vlc_thread
*th
= p
;
575 vlc_threadvar_set (thread_key
, th
);
577 th
->data
= th
->entry (th
->data
);
578 vlc_thread_cleanup (th
);
582 static int vlc_clone_attr (vlc_thread_t
*p_handle
, bool detached
,
583 void *(*entry
) (void *), void *data
, int priority
)
585 struct vlc_thread
*th
= malloc (sizeof (*th
));
586 if (unlikely(th
== NULL
))
590 th
->detached
= detached
;
591 th
->killable
= false; /* not until vlc_entry() ! */
597 /* When using the MSVCRT C library you have to use the _beginthreadex
598 * function instead of CreateThread, otherwise you'll end up with
599 * memory leaks and the signal functions not working (see Microsoft
600 * Knowledge Base, article 104641) */
603 h
= _beginthreadex (NULL
, 0, vlc_entry
, th
, CREATE_SUSPENDED
, NULL
);
613 th
->cancel_event
= CreateEvent (NULL
, FALSE
, FALSE
, NULL
);
614 if (th
->cancel_event
== NULL
)
620 /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
621 * Thread handles act up, too. */
622 hThread
= CreateThread (NULL
, 128*1024, vlc_entry
, th
,
623 CREATE_SUSPENDED
, NULL
);
626 CloseHandle (th
->cancel_event
);
632 /* Thread is suspended, so we can safely set th->id */
634 if (p_handle
!= NULL
)
638 SetThreadPriority (hThread
, priority
);
640 ResumeThread (hThread
);
645 int vlc_clone (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
646 void *data
, int priority
)
648 return vlc_clone_attr (p_handle
, false, entry
, data
, priority
);
651 void vlc_join (vlc_thread_t th
, void **result
)
655 while (vlc_WaitForSingleObject (th
->id
, INFINITE
)
656 == WAIT_IO_COMPLETION
);
660 CloseHandle (th
->id
);
662 CloseHandle (th
->cancel_event
);
667 int vlc_clone_detach (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
668 void *data
, int priority
)
671 if (p_handle
== NULL
)
674 return vlc_clone_attr (p_handle
, true, entry
, data
, priority
);
677 int vlc_set_priority (vlc_thread_t th
, int priority
)
679 if (!SetThreadPriority (th
->id
, priority
))
684 /*** Thread cancellation ***/
686 /* APC procedure for thread cancellation */
687 static void CALLBACK
vlc_cancel_self (ULONG_PTR self
)
689 struct vlc_thread
*th
= (void *)self
;
691 if (likely(th
!= NULL
))
695 void vlc_cancel (vlc_thread_t th
)
698 QueueUserAPC (vlc_cancel_self
, th
->id
, (uintptr_t)th
);
700 SetEvent (th
->cancel_event
);
704 int vlc_savecancel (void)
706 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
708 return false; /* Main thread - cannot be cancelled anyway */
710 int state
= th
->killable
;
711 th
->killable
= false;
715 void vlc_restorecancel (int state
)
717 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
718 assert (state
== false || state
== true);
721 return; /* Main thread - cannot be cancelled anyway */
723 assert (!th
->killable
);
724 th
->killable
= state
!= 0;
727 void vlc_testcancel (void)
729 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
731 return; /* Main thread - cannot be cancelled anyway */
733 if (th
->killable
&& th
->killed
)
735 for (vlc_cleanup_t
*p
= th
->cleaners
; p
!= NULL
; p
= p
->next
)
738 th
->data
= NULL
; /* TODO: special value? */
739 vlc_thread_cleanup (th
);
748 void vlc_control_cancel (int cmd
, ...)
750 /* NOTE: This function only modifies thread-specific data, so there is no
751 * need to lock anything. */
754 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
756 return; /* Main thread - cannot be cancelled anyway */
761 case VLC_CLEANUP_PUSH
:
763 /* cleaner is a pointer to the caller stack, no need to allocate
764 * and copy anything. As a nice side effect, this cannot fail. */
765 vlc_cleanup_t
*cleaner
= va_arg (ap
, vlc_cleanup_t
*);
766 cleaner
->next
= th
->cleaners
;
767 th
->cleaners
= cleaner
;
771 case VLC_CLEANUP_POP
:
773 th
->cleaners
= th
->cleaners
->next
;
784 /* We don't need the real date, just the value of a high precision timer */
785 LARGE_INTEGER counter
, freq
;
786 if (!QueryPerformanceCounter (&counter
)
787 || !QueryPerformanceFrequency (&freq
))
790 /* Convert to from (1/freq) to microsecond resolution */
791 /* We need to split the division to avoid 63-bits overflow */
792 lldiv_t d
= lldiv (counter
.QuadPart
, freq
.QuadPart
);
794 return (d
.quot
* 1000000) + ((d
.rem
* 1000000) / freq
.QuadPart
);
798 void mwait (mtime_t deadline
)
803 while ((delay
= (deadline
- mdate())) > 0)
806 if (unlikely(delay
> 0x7fffffff))
814 void msleep (mtime_t delay
)
816 mwait (mdate () + delay
);
829 void (*func
) (void *);
834 static void CALLBACK
vlc_timer_do (void *val
, BOOLEAN timeout
)
836 struct vlc_timer
*timer
= val
;
839 timer
->func (timer
->data
);
842 static void CALLBACK
vlc_timer_do (unsigned timer_id
, unsigned msg
,
843 DWORD_PTR user
, DWORD_PTR unused1
,
846 struct vlc_timer
*timer
= (struct vlc_timer
*) user
;
847 assert (timer_id
== timer
->id
);
852 timer
->func (timer
->data
);
856 mtime_t interval
= timer
->interval
* 1000;
857 vlc_timer_schedule (timer
, false, interval
, interval
);
862 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
864 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
871 timer
->handle
= INVALID_HANDLE_VALUE
;
880 void vlc_timer_destroy (vlc_timer_t timer
)
883 if (timer
->handle
!= INVALID_HANDLE_VALUE
)
884 DeleteTimerQueueTimer (NULL
, timer
->handle
, INVALID_HANDLE_VALUE
);
887 timeKillEvent (timer
->id
);
888 /* FIXME: timers that have not yet completed will trigger use-after-free */
893 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
894 mtime_t value
, mtime_t interval
)
897 if (timer
->handle
!= INVALID_HANDLE_VALUE
)
899 DeleteTimerQueueTimer (NULL
, timer
->handle
, NULL
);
900 timer
->handle
= INVALID_HANDLE_VALUE
;
905 timeKillEvent (timer
->id
);
915 value
= (value
+ 999) / 1000;
916 interval
= (interval
+ 999) / 1000;
919 if (!CreateTimerQueueTimer (&timer
->handle
, NULL
, vlc_timer_do
, timer
,
920 value
, interval
, WT_EXECUTEDEFAULT
))
923 timeGetDevCaps (&caps
, sizeof(caps
));
925 unsigned delay
= value
;
926 delay
= __MAX(delay
, caps
.wPeriodMin
);
927 delay
= __MIN(delay
, caps
.wPeriodMax
);
929 unsigned event
= TIME_ONESHOT
;
931 if (interval
== delay
)
932 event
= TIME_PERIODIC
;
934 timer
->interval
= interval
;
936 timer
->id
= timeSetEvent (delay
, delay
/ 20, vlc_timer_do
, (DWORD
) timer
,
943 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
951 unsigned vlc_GetCPUCount (void)
957 if (GetProcessAffinityMask (GetCurrentProcess(), &process
, &system
))
958 return popcount (system
);