1 /*****************************************************************************
2 * w32thread.c : Win32 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2009 the VideoLAN team
7 * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * 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>
38 static vlc_threadvar_t cancel_key;
41 * Per-thread cancellation data
43 typedef struct vlc_cancel_t
45 vlc_cleanup_t *cleaners;
54 # define VLC_CANCEL_INIT { NULL, true, false }
56 # define VLC_CANCEL_INIT { NULL, NULL; true, false }
60 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy);
62 static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles,
65 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
68 /* Main thread - cannot be cancelled anyway */
69 return WaitForMultipleObjects (count, handles, FALSE, delay);
71 HANDLE new_handles[count + 1];
72 memcpy(new_handles, handles, count * sizeof(HANDLE));
73 new_handles[count] = nfo->cancel_event;
74 DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE,
76 if (result == WAIT_OBJECT_0 + count)
78 vlc_cancel_self (NULL);
79 return WAIT_IO_COMPLETION;
87 DWORD SleepEx (DWORD dwMilliseconds, BOOL bAlertable)
91 DWORD result = vlc_cancelable_wait (0, NULL, dwMilliseconds);
92 return (result == WAIT_TIMEOUT) ? 0 : WAIT_IO_COMPLETION;
96 Sleep(dwMilliseconds);
101 DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds,
106 /* The MSDN documentation specifies different return codes,
107 * but in practice they are the same. We just check that it
109 #if WAIT_ABANDONED != WAIT_ABANDONED_0
110 # error Windows headers changed, code needs to be rewritten!
112 return vlc_cancelable_wait (1, &hHandle, dwMilliseconds);
116 return WaitForSingleObject (hHandle, dwMilliseconds);
120 DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles,
121 BOOL bWaitAll, DWORD dwMilliseconds,
126 /* We do not support the bWaitAll case */
128 return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds);
132 return WaitForMultipleObjects (nCount, lpHandles, bWaitAll,
138 static vlc_mutex_t super_mutex;
140 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
147 case DLL_PROCESS_ATTACH:
148 vlc_mutex_init (&super_mutex);
149 vlc_threadvar_create (&cancel_key, free);
152 case DLL_PROCESS_DETACH:
153 vlc_threadvar_delete( &cancel_key );
154 vlc_mutex_destroy (&super_mutex);
161 void vlc_mutex_init( vlc_mutex_t *p_mutex )
163 /* This creates a recursive mutex. This is OK as fast mutexes have
164 * no defined behavior in case of recursive locking. */
165 InitializeCriticalSection (&p_mutex->mutex);
166 p_mutex->initialized = 1;
170 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
172 InitializeCriticalSection( &p_mutex->mutex );
173 p_mutex->initialized = 1;
178 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
180 assert (InterlockedExchange (&p_mutex->initialized, -1) == 1);
181 DeleteCriticalSection (&p_mutex->mutex);
184 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
186 if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
187 { /* ^^ We could also lock super_mutex all the time... sluggish */
188 assert (p_mutex != &super_mutex); /* this one cannot be static */
190 vlc_mutex_lock (&super_mutex);
191 if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
192 vlc_mutex_init (p_mutex);
193 /* FIXME: destroy the mutex some time... */
194 vlc_mutex_unlock (&super_mutex);
196 assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
197 EnterCriticalSection (&p_mutex->mutex);
200 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
202 if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
203 { /* ^^ We could also lock super_mutex all the time... sluggish */
204 assert (p_mutex != &super_mutex); /* this one cannot be static */
206 vlc_mutex_lock (&super_mutex);
207 if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
208 vlc_mutex_init (p_mutex);
209 /* FIXME: destroy the mutex some time... */
210 vlc_mutex_unlock (&super_mutex);
212 assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
213 return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
216 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
218 assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
219 LeaveCriticalSection (&p_mutex->mutex);
222 /*** Condition variables ***/
223 void vlc_cond_init( vlc_cond_t *p_condvar )
225 /* Create a manual-reset event (manual reset is needed for broadcast). */
226 *p_condvar = CreateEvent (NULL, TRUE, FALSE, NULL);
231 void vlc_cond_destroy (vlc_cond_t *p_condvar)
233 CloseHandle (*p_condvar);
236 void vlc_cond_signal (vlc_cond_t *p_condvar)
238 /* NOTE: This will cause a broadcast, that is wrong.
239 * This will also wake up the next waiting thread if no threads are yet
240 * waiting, which is also wrong. However both of these issues are allowed
241 * by the provision for spurious wakeups. Better have too many wakeups
242 * than too few (= deadlocks). */
243 SetEvent (*p_condvar);
246 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
248 SetEvent (*p_condvar);
251 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
258 LeaveCriticalSection (&p_mutex->mutex);
259 result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE);
260 EnterCriticalSection (&p_mutex->mutex);
262 while (result == WAIT_IO_COMPLETION);
264 assert (result != WAIT_ABANDONED); /* another thread failed to cleanup! */
265 assert (result != WAIT_FAILED);
266 ResetEvent (*p_condvar);
269 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
278 mtime_t total = (deadline - mdate ())/1000;
282 DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
283 LeaveCriticalSection (&p_mutex->mutex);
284 result = WaitForSingleObjectEx (*p_condvar, delay, TRUE);
285 EnterCriticalSection (&p_mutex->mutex);
287 while (result == WAIT_IO_COMPLETION);
289 assert (result != WAIT_ABANDONED);
290 assert (result != WAIT_FAILED);
291 ResetEvent (*p_condvar);
293 return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
296 /*** Thread-specific variables (TLS) ***/
297 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
299 #warning FIXME: use destr() callback and stop leaking!
301 return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
304 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
310 * Sets a thread-local variable.
311 * @param key thread-local variable key (created with vlc_threadvar_create())
312 * @param value new value for the variable for the calling thread
313 * @return 0 on success, a system error code otherwise.
315 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
317 return TlsSetValue (key, value) ? ENOMEM : 0;
321 * Gets the value of a thread-local variable for the calling thread.
322 * This function cannot fail.
323 * @return the value associated with the given variable for the calling
324 * or NULL if there is no value.
326 void *vlc_threadvar_get (vlc_threadvar_t key)
328 return TlsGetValue (key);
333 static unsigned __stdcall vlc_entry (void *data)
335 vlc_cancel_t cancel_data = VLC_CANCEL_INIT;
336 vlc_thread_t self = data;
338 cancel_data.cancel_event = self->cancel_event;
341 vlc_threadvar_set (cancel_key, &cancel_data);
342 self->data = self->entry (self->data);
346 int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
349 /* When using the MSVCRT C library you have to use the _beginthreadex
350 * function instead of CreateThread, otherwise you'll end up with
351 * memory leaks and the signal functions not working (see Microsoft
352 * Knowledge Base, article 104641) */
354 vlc_thread_t th = malloc (sizeof (*th));
361 #if defined( UNDER_CE )
362 th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
363 if (th->cancel_event == NULL)
368 hThread = CreateThread (NULL, 128*1024, vlc_entry, th, CREATE_SUSPENDED, NULL);
370 hThread = (HANDLE)(uintptr_t)
371 _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
377 /* Thread closes the handle when exiting, duplicate it here
378 * to be on the safe side when joining. */
379 if (!DuplicateHandle (GetCurrentProcess (), hThread,
380 GetCurrentProcess (), &th->handle, 0, FALSE,
381 DUPLICATE_SAME_ACCESS))
383 CloseHandle (hThread);
388 th->handle = hThread;
391 ResumeThread (hThread);
393 SetThreadPriority (hThread, priority);
402 void vlc_join (vlc_thread_t handle, void **result)
406 while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE)
407 == WAIT_IO_COMPLETION);
409 CloseHandle (handle->handle);
411 *result = handle->data;
413 CloseHandle (handle->cancel_event);
419 /*** Thread cancellation ***/
421 /* APC procedure for thread cancellation */
422 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
425 vlc_control_cancel (VLC_DO_CANCEL);
428 void vlc_cancel (vlc_thread_t thread_id)
431 QueueUserAPC (vlc_cancel_self, thread_id->handle, 0);
433 SetEvent (thread_id->cancel_event);
437 int vlc_savecancel (void)
441 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
443 return false; /* Main thread - cannot be cancelled anyway */
445 state = nfo->killable;
446 nfo->killable = false;
450 void vlc_restorecancel (int state)
452 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
453 assert (state == false || state == true);
456 return; /* Main thread - cannot be cancelled anyway */
458 assert (!nfo->killable);
459 nfo->killable = state != 0;
462 void vlc_testcancel (void)
464 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
466 return; /* Main thread - cannot be cancelled anyway */
468 if (nfo->killable && nfo->killed)
470 for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
480 void vlc_control_cancel (int cmd, ...)
482 /* NOTE: This function only modifies thread-specific data, so there is no
483 * need to lock anything. */
486 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
488 return; /* Main thread - cannot be cancelled anyway */
497 case VLC_CLEANUP_PUSH:
499 /* cleaner is a pointer to the caller stack, no need to allocate
500 * and copy anything. As a nice side effect, this cannot fail. */
501 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
502 cleaner->next = nfo->cleaners;
503 nfo->cleaners = cleaner;
507 case VLC_CLEANUP_POP:
509 nfo->cleaners = nfo->cleaners->next;