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 int 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 int 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 int 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);
227 return *p_condvar ? 0 : ENOMEM;
230 void vlc_cond_destroy (vlc_cond_t *p_condvar)
232 CloseHandle (*p_condvar);
235 void vlc_cond_signal (vlc_cond_t *p_condvar)
237 /* NOTE: This will cause a broadcast, that is wrong.
238 * This will also wake up the next waiting thread if no threads are yet
239 * waiting, which is also wrong. However both of these issues are allowed
240 * by the provision for spurious wakeups. Better have too many wakeups
241 * than too few (= deadlocks). */
242 SetEvent (*p_condvar);
245 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
247 SetEvent (*p_condvar);
250 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
257 LeaveCriticalSection (&p_mutex->mutex);
258 result = WaitForSingleObjectEx (*p_condvar, INFINITE, TRUE);
259 EnterCriticalSection (&p_mutex->mutex);
261 while (result == WAIT_IO_COMPLETION);
263 assert (result != WAIT_ABANDONED); /* another thread failed to cleanup! */
264 assert (result != WAIT_FAILED);
265 ResetEvent (*p_condvar);
268 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
277 mtime_t total = (deadline - mdate ())/1000;
281 DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
282 LeaveCriticalSection (&p_mutex->mutex);
283 result = WaitForSingleObjectEx (*p_condvar, delay, TRUE);
284 EnterCriticalSection (&p_mutex->mutex);
286 while (result == WAIT_IO_COMPLETION);
288 assert (result != WAIT_ABANDONED);
289 assert (result != WAIT_FAILED);
290 ResetEvent (*p_condvar);
292 return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
295 /*** Thread-specific variables (TLS) ***/
296 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
298 #warning FIXME: use destr() callback and stop leaking!
300 return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
303 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
309 * Sets a thread-local variable.
310 * @param key thread-local variable key (created with vlc_threadvar_create())
311 * @param value new value for the variable for the calling thread
312 * @return 0 on success, a system error code otherwise.
314 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
316 return TlsSetValue (key, value) ? ENOMEM : 0;
320 * Gets the value of a thread-local variable for the calling thread.
321 * This function cannot fail.
322 * @return the value associated with the given variable for the calling
323 * or NULL if there is no value.
325 void *vlc_threadvar_get (vlc_threadvar_t key)
327 return TlsGetValue (key);
332 static unsigned __stdcall vlc_entry (void *data)
334 vlc_cancel_t cancel_data = VLC_CANCEL_INIT;
335 vlc_thread_t self = data;
337 cancel_data.cancel_event = self->cancel_event;
340 vlc_threadvar_set (cancel_key, &cancel_data);
341 self->data = self->entry (self->data);
345 int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
348 /* When using the MSVCRT C library you have to use the _beginthreadex
349 * function instead of CreateThread, otherwise you'll end up with
350 * memory leaks and the signal functions not working (see Microsoft
351 * Knowledge Base, article 104641) */
353 vlc_thread_t th = malloc (sizeof (*th));
360 #if defined( UNDER_CE )
361 th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
362 if (th->cancel_event == NULL)
367 hThread = CreateThread (NULL, 128*1024, vlc_entry, th, CREATE_SUSPENDED, NULL);
369 hThread = (HANDLE)(uintptr_t)
370 _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
376 /* Thread closes the handle when exiting, duplicate it here
377 * to be on the safe side when joining. */
378 if (!DuplicateHandle (GetCurrentProcess (), hThread,
379 GetCurrentProcess (), &th->handle, 0, FALSE,
380 DUPLICATE_SAME_ACCESS))
382 CloseHandle (hThread);
387 th->handle = hThread;
390 ResumeThread (hThread);
392 SetThreadPriority (hThread, priority);
401 void vlc_join (vlc_thread_t handle, void **result)
405 while (WaitForSingleObjectEx (handle->handle, INFINITE, TRUE)
406 == WAIT_IO_COMPLETION);
408 CloseHandle (handle->handle);
410 *result = handle->data;
412 CloseHandle (handle->cancel_event);
418 /*** Thread cancellation ***/
420 /* APC procedure for thread cancellation */
421 static void CALLBACK vlc_cancel_self (ULONG_PTR dummy)
424 vlc_control_cancel (VLC_DO_CANCEL);
427 void vlc_cancel (vlc_thread_t thread_id)
430 QueueUserAPC (vlc_cancel_self, thread_id->handle, 0);
432 SetEvent (thread_id->cancel_event);
436 int vlc_savecancel (void)
440 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
442 return false; /* Main thread - cannot be cancelled anyway */
444 state = nfo->killable;
445 nfo->killable = false;
449 void vlc_restorecancel (int state)
451 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
452 assert (state == false || state == true);
455 return; /* Main thread - cannot be cancelled anyway */
457 assert (!nfo->killable);
458 nfo->killable = state != 0;
461 void vlc_testcancel (void)
463 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
465 return; /* Main thread - cannot be cancelled anyway */
467 if (nfo->killable && nfo->killed)
469 for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next)
479 void vlc_control_cancel (int cmd, ...)
481 /* NOTE: This function only modifies thread-specific data, so there is no
482 * need to lock anything. */
485 vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key);
487 return; /* Main thread - cannot be cancelled anyway */
496 case VLC_CLEANUP_PUSH:
498 /* cleaner is a pointer to the caller stack, no need to allocate
499 * and copy anything. As a nice side effect, this cannot fail. */
500 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
501 cleaner->next = nfo->cleaners;
502 nfo->cleaners = cleaner;
506 case VLC_CLEANUP_POP:
508 nfo->cleaners = nfo->cleaners->next;