* Gildas Bazin <gbazin@netcourrier.com>
* Clément Sténac
* Rémi Denis-Courmont
+ * Pierre Ynard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
+#ifdef UNDER_CE
+# include <mmsystem.h>
+#endif
static vlc_threadvar_t cancel_key;
#endif
static vlc_mutex_t super_mutex;
+static vlc_cond_t super_variable;
BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
{
{
case DLL_PROCESS_ATTACH:
vlc_mutex_init (&super_mutex);
+ vlc_cond_init (&super_variable);
vlc_threadvar_create (&cancel_key, free);
break;
case DLL_PROCESS_DETACH:
vlc_threadvar_delete( &cancel_key );
+ vlc_cond_destroy (&super_variable);
vlc_mutex_destroy (&super_mutex);
break;
}
/* This creates a recursive mutex. This is OK as fast mutexes have
* no defined behavior in case of recursive locking. */
InitializeCriticalSection (&p_mutex->mutex);
- p_mutex->initialized = 1;
+ p_mutex->dynamic = true;
}
void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
{
InitializeCriticalSection( &p_mutex->mutex );
- p_mutex->initialized = 1;
+ p_mutex->dynamic = true;
}
void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
{
- assert (InterlockedExchange (&p_mutex->initialized, -1) == 1);
+ assert (p_mutex->dynamic);
DeleteCriticalSection (&p_mutex->mutex);
}
void vlc_mutex_lock (vlc_mutex_t *p_mutex)
{
- if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
- { /* ^^ We could also lock super_mutex all the time... sluggish */
+ if (!p_mutex->dynamic)
+ { /* static mutexes */
assert (p_mutex != &super_mutex); /* this one cannot be static */
vlc_mutex_lock (&super_mutex);
- if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
- vlc_mutex_init (p_mutex);
- /* FIXME: destroy the mutex some time... */
+ while (p_mutex->locked)
+ {
+ p_mutex->contention++;
+ vlc_cond_wait (&super_variable, &super_mutex);
+ p_mutex->contention--;
+ }
+ p_mutex->locked = true;
vlc_mutex_unlock (&super_mutex);
+ return;
}
- assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
+
EnterCriticalSection (&p_mutex->mutex);
}
int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
{
- if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
- { /* ^^ We could also lock super_mutex all the time... sluggish */
- assert (p_mutex != &super_mutex); /* this one cannot be static */
+ if (!p_mutex->dynamic)
+ { /* static mutexes */
+ int ret = EBUSY;
+ assert (p_mutex != &super_mutex); /* this one cannot be static */
vlc_mutex_lock (&super_mutex);
- if (InterlockedCompareExchange (&p_mutex->initialized, 0, 0) == 0)
- vlc_mutex_init (p_mutex);
- /* FIXME: destroy the mutex some time... */
+ if (!p_mutex->locked)
+ {
+ p_mutex->locked = true;
+ ret = 0;
+ }
vlc_mutex_unlock (&super_mutex);
+ return ret;
}
- assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
+
return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
}
void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
{
- assert (InterlockedExchange (&p_mutex->initialized, 1) == 1);
+ if (!p_mutex->dynamic)
+ { /* static mutexes */
+ assert (p_mutex != &super_mutex); /* this one cannot be static */
+
+ vlc_mutex_lock (&super_mutex);
+ assert (p_mutex->locked);
+ p_mutex->locked = false;
+ if (p_mutex->contention)
+ vlc_cond_broadcast (&super_variable);
+ vlc_mutex_unlock (&super_mutex);
+ return;
+ }
+
LeaveCriticalSection (&p_mutex->mutex);
}
return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
}
+/*** Semaphore ***/
+void vlc_sem_init (vlc_sem_t *sem, unsigned value)
+{
+ *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
+ if (*sem == NULL)
+ abort ();
+}
+
+void vlc_sem_destroy (vlc_sem_t *sem)
+{
+ CloseHandle (*sem);
+}
+
+int vlc_sem_post (vlc_sem_t *sem)
+{
+ ReleaseSemaphore (*sem, 1, NULL);
+ return 0; /* FIXME */
+}
+
+void vlc_sem_wait (vlc_sem_t *sem)
+{
+ DWORD result;
+
+ do
+ {
+ vlc_testcancel ();
+ result = WaitForSingleObjectEx (*sem, INFINITE, TRUE);
+ }
+ while (result == WAIT_IO_COMPLETION);
+}
+
/*** Read/write locks */
/* SRW (Slim Read Write) locks are available in Vista+ only */
void vlc_rwlock_init (vlc_rwlock_t *lock)
int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
{
#warning FIXME: use destr() callback and stop leaking!
+ VLC_UNUSED( destr );
+
*p_tls = TlsAlloc();
return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0;
}
{
void * (*func) (void *);
void * data;
+#ifdef UNDER_CE
+ HANDLE cancel_event;
+#endif
};
static unsigned __stdcall vlc_entry (void *p)
free (p);
#ifdef UNDER_CE
- cancel_data.cancel_event = data.handle->cancel_event;
+ cancel_data.cancel_event = data.cancel_event;
#endif
vlc_threadvar_set (cancel_key, &cancel_data);
int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data,
int priority)
{
- /* When using the MSVCRT C library you have to use the _beginthreadex
- * function instead of CreateThread, otherwise you'll end up with
- * memory leaks and the signal functions not working (see Microsoft
- * Knowledge Base, article 104641) */
+ int err = ENOMEM;
HANDLE hThread;
struct vlc_entry_data *entry_data = malloc (sizeof (*entry_data));
entry_data->func = entry;
entry_data->data = data;
-#if defined( UNDER_CE )
+#ifndef UNDER_CE
+ /* When using the MSVCRT C library you have to use the _beginthreadex
+ * function instead of CreateThread, otherwise you'll end up with
+ * memory leaks and the signal functions not working (see Microsoft
+ * Knowledge Base, article 104641) */
+ hThread = (HANDLE)(uintptr_t)
+ _beginthreadex (NULL, 0, vlc_entry, entry_data, CREATE_SUSPENDED, NULL);
+ if (! hThread)
+ {
+ err = errno;
+ goto error;
+ }
+
+ /* Thread closes the handle when exiting, duplicate it here
+ * to be on the safe side when joining. */
+ if (!DuplicateHandle (GetCurrentProcess (), hThread,
+ GetCurrentProcess (), p_handle, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle (hThread);
+ goto error;
+ }
+
+#else
+ vlc_thread_t th = malloc (sizeof (*th));
+ if (th == NULL)
+ goto error;
th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
if (th->cancel_event == NULL)
{
- free (entry_data);
- return errno;
+ free (th);
+ goto error;
}
- hThread = CreateThread (NULL, 128*1024, vlc_entry, entry_data, CREATE_SUSPENDED, NULL);
-#else
- hThread = (HANDLE)(uintptr_t)
- _beginthreadex (NULL, 0, vlc_entry, entry_data, CREATE_SUSPENDED, NULL);
-#endif
+ entry_data->cancel_event = th->cancel_event;
- if (hThread)
+ /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
+ * Thread handles act up, too. */
+ th->handle = CreateThread (NULL, 128*1024, vlc_entry, entry_data,
+ CREATE_SUSPENDED, NULL);
+ if (th->handle == NULL)
{
-#ifndef UNDER_CE
- /* Thread closes the handle when exiting, duplicate it here
- * to be on the safe side when joining. */
- if (!DuplicateHandle (GetCurrentProcess (), hThread,
- GetCurrentProcess (), p_handle, 0, FALSE,
- DUPLICATE_SAME_ACCESS))
- {
- CloseHandle (hThread);
- free (entry_data);
- return ENOMEM;
- }
-#else
- th->handle = hThread;
+ CloseHandle (th->cancel_event);
+ free (th);
+ goto error;
+ }
+
+ *p_handle = th;
+ hThread = th->handle;
+
#endif
- ResumeThread (hThread);
- if (priority)
- SetThreadPriority (hThread, priority);
- return 0;
- }
- return errno;
+ ResumeThread (hThread);
+ if (priority)
+ SetThreadPriority (hThread, priority);
+
+ return 0;
+
+error:
+ free (entry_data);
+ return err;
}
void vlc_join (vlc_thread_t handle, void **result)
{
+#ifdef UNDER_CE
+# define handle handle->handle
+#endif
do
vlc_testcancel ();
while (WaitForSingleObjectEx (handle, INFINITE, TRUE)
CloseHandle (handle);
assert (result == NULL); /* <- FIXME if ever needed */
#ifdef UNDER_CE
+# undef handle
CloseHandle (handle->cancel_event);
+ free (handle);
#endif
}
void vlc_detach (vlc_thread_t handle)
{
+#ifndef UNDER_CE
CloseHandle (handle);
+#else
+ /* FIXME: handle->cancel_event leak */
+ CloseHandle (handle->handle);
+ free (handle);
+#endif
}
/*** Thread cancellation ***/
/*** Timers ***/
+struct vlc_timer
+{
+#ifndef UNDER_CE
+ HANDLE handle;
+#else
+ unsigned id;
+ unsigned interval;
+#endif
+ void (*func) (void *);
+ void *data;
+};
+
+#ifndef UNDER_CE
static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
{
- vlc_timer_t *id = val;
+ struct vlc_timer *timer = val;
assert (timeout);
- id->func (id->data);
+ timer->func (timer->data);
}
+#else
+static void CALLBACK vlc_timer_do (unsigned timer_id, unsigned msg,
+ DWORD_PTR user, DWORD_PTR unused1,
+ DWORD_PTR unused2)
+{
+ struct vlc_timer *timer = (struct vlc_timer *) user;
+ assert (timer_id == timer->id);
+ (void) msg;
+ (void) unused1;
+ (void) unused2;
+
+ timer->func (timer->data);
+
+ if (timer->interval)
+ {
+ mtime_t interval = timer->interval * 1000;
+ vlc_timer_schedule (timer, false, interval, interval);
+ }
+}
+#endif
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
{
- id->func = func;
- id->data = data;
- id->handle = INVALID_HANDLE_VALUE;
+ struct vlc_timer *timer = malloc (sizeof (*timer));
+
+ if (timer == NULL)
+ return ENOMEM;
+ timer->func = func;
+ timer->data = data;
+#ifndef UNDER_CE
+ timer->handle = INVALID_HANDLE_VALUE;
+#else
+ timer->id = 0;
+ timer->interval = 0;
+#endif
+ *id = timer;
return 0;
}
-void vlc_timer_destroy (vlc_timer_t *id)
+void vlc_timer_destroy (vlc_timer_t timer)
{
- if (id->handle != INVALID_HANDLE_VALUE)
- DeleteTimerQueueTimer (NULL, id->handle, INVALID_HANDLE_VALUE);
+#ifndef UNDER_CE
+ if (timer->handle != INVALID_HANDLE_VALUE)
+ DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
+#else
+ if (timer->id)
+ timeKillEvent (timer->id);
+ /* FIXME: timers that have not yet completed will trigger use-after-free */
+#endif
+ free (timer);
}
-void vlc_timer_schedule (vlc_timer_t *id, bool absolute,
+void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
mtime_t value, mtime_t interval)
{
- if (id->handle != INVALID_HANDLE_VALUE)
+#ifndef UNDER_CE
+ if (timer->handle != INVALID_HANDLE_VALUE)
{
- DeleteTimerQueueTimer (NULL, id->handle, NULL);
- id->handle = INVALID_HANDLE_VALUE;
+ DeleteTimerQueueTimer (NULL, timer->handle, NULL);
+ timer->handle = INVALID_HANDLE_VALUE;
}
+#else
+ if (timer->id)
+ {
+ timeKillEvent (timer->id);
+ timer->id = 0;
+ timer->interval = 0;
+ }
+#endif
if (value == 0)
return; /* Disarm */
value -= mdate ();
value = (value + 999) / 1000;
interval = (interval + 999) / 1000;
- if (!CreateTimerQueueTimer (&id->handle, NULL, vlc_timer_do, id, value,
- interval, WT_EXECUTEDEFAULT))
+
+#ifndef UNDER_CE
+ if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
+ value, interval, WT_EXECUTEDEFAULT))
+#else
+ TIMECAPS caps;
+ timeGetDevCaps (&caps, sizeof(caps));
+
+ unsigned delay = value;
+ delay = __MAX(delay, caps.wPeriodMin);
+ delay = __MIN(delay, caps.wPeriodMax);
+
+ unsigned event = TIME_ONESHOT;
+
+ if (interval == delay)
+ event = TIME_PERIODIC;
+ else if (interval)
+ timer->interval = interval;
+
+ timer->id = timeSetEvent (delay, delay / 20, vlc_timer_do, (DWORD) timer,
+ event);
+ if (!timer->id)
+#endif
abort ();
}
-unsigned vlc_timer_getoverrun (const vlc_timer_t *id)
+unsigned vlc_timer_getoverrun (vlc_timer_t timer)
{
- (void)id;
+ (void)timer;
return 0;
}