X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fwin32%2Fthread.c;h=965db9906e05d3ebbb66ad1ad61515c8dbf095ac;hb=236a276618fc83c18e100ace359c51ba4734010c;hp=08de7125c62b0a9dcb783b4dfe6b3c3548dc737f;hpb=a160b8589331960e47702e382794d492ee480946;p=vlc diff --git a/src/win32/thread.c b/src/win32/thread.c index 08de7125c6..965db9906e 100644 --- a/src/win32/thread.c +++ b/src/win32/thread.c @@ -39,132 +39,120 @@ #ifdef UNDER_CE # include #endif +#include "config/configuration.h" -static vlc_threadvar_t cancel_key; +static vlc_threadvar_t thread_key; /** - * Per-thread cancellation data + * Per-thread data */ -typedef struct vlc_cancel_t +struct vlc_thread { - vlc_cleanup_t *cleaners; + HANDLE id; #ifdef UNDER_CE HANDLE cancel_event; #endif + + bool detached; bool killable; bool killed; -} vlc_cancel_t; + vlc_cleanup_t *cleaners; -#ifndef UNDER_CE -# define VLC_CANCEL_INIT { NULL, true, false } -#else -# define VLC_CANCEL_INIT { NULL, NULL, true, false } -#endif + void *(*entry) (void *); + void *data; +}; -#ifdef UNDER_CE -static void CALLBACK vlc_cancel_self (ULONG_PTR dummy); +static CRITICAL_SECTION super_mutex; +static HANDLE super_cond; + +BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID); -static DWORD vlc_cancelable_wait (DWORD count, const HANDLE *handles, - DWORD delay) +BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) { - vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key); - if (nfo == NULL) - { - /* Main thread - cannot be cancelled anyway */ - return WaitForMultipleObjects (count, handles, FALSE, delay); - } - HANDLE new_handles[count + 1]; - memcpy(new_handles, handles, count * sizeof(HANDLE)); - new_handles[count] = nfo->cancel_event; - DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE, - delay); - if (result == WAIT_OBJECT_0 + count) - { - vlc_cancel_self (NULL); - return WAIT_IO_COMPLETION; - } - else + (void) hinstDll; + (void) lpvReserved; + + switch (fdwReason) { - return result; + case DLL_PROCESS_ATTACH: + super_cond = CreateEvent (NULL, TRUE, FALSE, NULL); + if (unlikely(!super_cond)) + return FALSE; + InitializeCriticalSection (&super_mutex); + vlc_threadvar_create (&thread_key, NULL); + vlc_rwlock_init (&config_lock); + break; + + case DLL_PROCESS_DETACH: + vlc_rwlock_destroy (&config_lock); + vlc_threadvar_delete (&thread_key); + DeleteCriticalSection (&super_mutex); + CloseHandle (super_cond); + break; } + return TRUE; } -DWORD SleepEx (DWORD dwMilliseconds, BOOL bAlertable) +static void CALLBACK vlc_cancel_self (ULONG_PTR); + +static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles, + DWORD delay) { - if (bAlertable) - { - DWORD result = vlc_cancelable_wait (0, NULL, dwMilliseconds); - return (result == WAIT_TIMEOUT) ? 0 : WAIT_IO_COMPLETION; - } - else + DWORD ret; +#ifdef UNDER_CE + HANDLE buf[count + 1]; + + struct vlc_thread *th = vlc_threadvar_get (thread_key); + if (th != NULL) { - Sleep(dwMilliseconds); - return 0; + memcpy (buf, handles, count * sizeof(HANDLE)); + buf[count++] = th->cancel_event; + handles = buf; } -} -DWORD WaitForSingleObjectEx (HANDLE hHandle, DWORD dwMilliseconds, - BOOL bAlertable) -{ - if (bAlertable) + if (count == 0) { - /* The MSDN documentation specifies different return codes, - * but in practice they are the same. We just check that it - * remains so. */ -#if WAIT_ABANDONED != WAIT_ABANDONED_0 -# error Windows headers changed, code needs to be rewritten! -#endif - return vlc_cancelable_wait (1, &hHandle, dwMilliseconds); + Sleep (delay); + ret = WAIT_TIMEOUT; } else - { - return WaitForSingleObject (hHandle, dwMilliseconds); - } -} + ret = WaitForMultipleObjects (count, handles, FALSE, delay); -DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles, - BOOL bWaitAll, DWORD dwMilliseconds, - BOOL bAlertable) -{ - if (bAlertable) + if ((th != NULL) && (ret == WAIT_OBJECT_0 + count - 1)) { - /* We do not support the bWaitAll case */ - assert (! bWaitAll); - return vlc_cancelable_wait (nCount, lpHandles, dwMilliseconds); + vlc_cancel_self ((uintptr_t)th); + ret = WAIT_IO_COMPLETION; } - else +#else + if (count == 0) { - return WaitForMultipleObjects (nCount, lpHandles, bWaitAll, - dwMilliseconds); + ret = SleepEx (delay, TRUE); + if (ret == 0) + ret = WAIT_TIMEOUT; } -} + else + ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE); #endif + /* We do not abandon objects... this would be a bug */ + assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret); -vlc_mutex_t super_mutex; -vlc_cond_t super_variable; + if (unlikely(ret == WAIT_FAILED)) + abort (); /* We are screwed! */ + return ret; +} -BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) +static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay) { - (void) hinstDll; - (void) lpvReserved; - - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - vlc_mutex_init (&super_mutex); - vlc_cond_init (&super_variable); - vlc_threadvar_create (&cancel_key, free); - break; + return vlc_WaitForMultipleObjects (1, &handle, delay); +} - case DLL_PROCESS_DETACH: - vlc_threadvar_delete( &cancel_key ); - vlc_cond_destroy (&super_variable); - vlc_mutex_destroy (&super_mutex); - break; - } - return TRUE; +static DWORD vlc_Sleep (DWORD delay) +{ + DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay); + return (ret != WAIT_TIMEOUT) ? ret : 0; } + /*** Mutexes ***/ void vlc_mutex_init( vlc_mutex_t *p_mutex ) { @@ -190,18 +178,19 @@ void vlc_mutex_destroy (vlc_mutex_t *p_mutex) void vlc_mutex_lock (vlc_mutex_t *p_mutex) { if (!p_mutex->dynamic) - { /* static mutexes */ - assert (p_mutex != &super_mutex); /* this one cannot be static */ - - vlc_mutex_lock (&super_mutex); + { /* static mutexes (inefficient on Windows) */ + EnterCriticalSection (&super_mutex); while (p_mutex->locked) { p_mutex->contention++; - vlc_cond_wait (&super_variable, &super_mutex); + LeaveCriticalSection (&super_mutex); + WaitForSingleObject (super_cond, INFINITE); + EnterCriticalSection (&super_mutex); + assert (p_mutex->contention > 0); p_mutex->contention--; } p_mutex->locked = true; - vlc_mutex_unlock (&super_mutex); + LeaveCriticalSection (&super_mutex); return; } @@ -214,14 +203,13 @@ int vlc_mutex_trylock (vlc_mutex_t *p_mutex) { /* static mutexes */ int ret = EBUSY; - assert (p_mutex != &super_mutex); /* this one cannot be static */ - vlc_mutex_lock (&super_mutex); + EnterCriticalSection (&super_mutex); if (!p_mutex->locked) { p_mutex->locked = true; ret = 0; } - vlc_mutex_unlock (&super_mutex); + LeaveCriticalSection (&super_mutex); return ret; } @@ -232,14 +220,12 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex) { if (!p_mutex->dynamic) { /* static mutexes */ - assert (p_mutex != &super_mutex); /* this one cannot be static */ - - vlc_mutex_lock (&super_mutex); + EnterCriticalSection (&super_mutex); assert (p_mutex->locked); p_mutex->locked = false; - if (p_mutex->contention) - vlc_cond_broadcast (&super_variable); - vlc_mutex_unlock (&super_mutex); + if (p_mutex->contention > 0) + SetEvent (super_cond); + LeaveCriticalSection (&super_mutex); return; } @@ -249,8 +235,8 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex) /*** Condition variables ***/ enum { + CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */ CLOCK_MONOTONIC, - CLOCK_REALTIME, }; static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock) @@ -279,16 +265,19 @@ void vlc_cond_destroy (vlc_cond_t *p_condvar) void vlc_cond_signal (vlc_cond_t *p_condvar) { - /* NOTE: This will cause a broadcast, that is wrong. - * This will also wake up the next waiting thread if no threads are yet - * waiting, which is also wrong. However both of these issues are allowed - * by the provision for spurious wakeups. Better have too many wakeups - * than too few (= deadlocks). */ - SetEvent (p_condvar->handle); + if (!p_condvar->handle) + return; + + /* This is suboptimal but works. */ + vlc_cond_broadcast (p_condvar); } void vlc_cond_broadcast (vlc_cond_t *p_condvar) { + if (!p_condvar->handle) + return; + + /* Wake all threads up (as the event HANDLE has manual reset) */ SetEvent (p_condvar->handle); } @@ -296,18 +285,21 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex) { DWORD result; - assert (p_mutex->dynamic); /* TODO */ + if (!p_condvar->handle) + { /* FIXME FIXME FIXME */ + msleep (50000); + return; + } + do { vlc_testcancel (); - LeaveCriticalSection (&p_mutex->mutex); - result = WaitForSingleObjectEx (p_condvar->handle, INFINITE, TRUE); - EnterCriticalSection (&p_mutex->mutex); + vlc_mutex_unlock (p_mutex); + result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE); + vlc_mutex_lock (p_mutex); } while (result == WAIT_IO_COMPLETION); - assert (result != WAIT_ABANDONED); /* another thread failed to cleanup! */ - assert (result != WAIT_FAILED); ResetEvent (p_condvar->handle); } @@ -316,7 +308,12 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex, { DWORD result; - assert (p_mutex->dynamic); /* TODO */ + if (!p_condvar->handle) + { /* FIXME FIXME FIXME */ + msleep (50000); + return 0; + } + do { vlc_testcancel (); @@ -324,28 +321,25 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex, mtime_t total; switch (p_condvar->clock) { - case CLOCK_MONOTONIC: - total = mdate(); - break; case CLOCK_REALTIME: /* FIXME? sub-second precision */ total = CLOCK_FREQ * time (NULL); break; default: - assert (0); + assert (p_condvar->clock == CLOCK_MONOTONIC); + total = mdate(); + break; } total = (deadline - total) / 1000; if( total < 0 ) total = 0; DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total; - LeaveCriticalSection (&p_mutex->mutex); - result = WaitForSingleObjectEx (p_condvar->handle, delay, TRUE); - EnterCriticalSection (&p_mutex->mutex); + vlc_mutex_unlock (p_mutex); + result = vlc_WaitForSingleObject (p_condvar->handle, delay); + vlc_mutex_lock (p_mutex); } while (result == WAIT_IO_COMPLETION); - assert (result != WAIT_ABANDONED); - assert (result != WAIT_FAILED); ResetEvent (p_condvar->handle); return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT; @@ -377,7 +371,7 @@ void vlc_sem_wait (vlc_sem_t *sem) do { vlc_testcancel (); - result = WaitForSingleObjectEx (*sem, INFINITE, TRUE); + result = vlc_WaitForSingleObject (*sem, INFINITE); } while (result == WAIT_IO_COMPLETION); } @@ -390,13 +384,10 @@ void vlc_rwlock_init (vlc_rwlock_t *lock) vlc_cond_init (&lock->read_wait); vlc_cond_init (&lock->write_wait); lock->readers = 0; /* active readers */ - lock->writers = 0; /* waiting or active writers */ + lock->writers = 0; /* waiting writers */ lock->writer = 0; /* ID of active writer */ } -/** - * Destroys an initialized unused read/write lock. - */ void vlc_rwlock_destroy (vlc_rwlock_t *lock) { vlc_cond_destroy (&lock->read_wait); @@ -404,286 +395,344 @@ void vlc_rwlock_destroy (vlc_rwlock_t *lock) vlc_mutex_destroy (&lock->mutex); } -/** - * Acquires a read/write lock for reading. Recursion is allowed. - */ void vlc_rwlock_rdlock (vlc_rwlock_t *lock) { vlc_mutex_lock (&lock->mutex); + /* Recursive read-locking is allowed. With the infos available: + * - the loosest possible condition (no active writer) is: + * (lock->writer != 0) + * - the strictest possible condition is: + * (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0)) + * or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0)) + */ while (lock->writer != 0) + { + assert (lock->readers == 0); vlc_cond_wait (&lock->read_wait, &lock->mutex); - if (lock->readers == ULONG_MAX) + } + if (unlikely(lock->readers == ULONG_MAX)) abort (); lock->readers++; vlc_mutex_unlock (&lock->mutex); } -/** - * Acquires a read/write lock for writing. Recursion is not allowed. - */ +static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock) +{ + vlc_mutex_lock (&lock->mutex); + assert (lock->readers > 0); + + /* If there are no readers left, wake up a writer. */ + if (--lock->readers == 0 && lock->writers > 0) + vlc_cond_signal (&lock->write_wait); + vlc_mutex_unlock (&lock->mutex); +} + void vlc_rwlock_wrlock (vlc_rwlock_t *lock) { vlc_mutex_lock (&lock->mutex); - if (lock->writers == ULONG_MAX) + if (unlikely(lock->writers == ULONG_MAX)) abort (); lock->writers++; + /* Wait until nobody owns the lock in either way. */ while ((lock->readers > 0) || (lock->writer != 0)) vlc_cond_wait (&lock->write_wait, &lock->mutex); lock->writers--; + assert (lock->writer == 0); lock->writer = GetCurrentThreadId (); vlc_mutex_unlock (&lock->mutex); } -/** - * Releases a read/write lock. - */ -void vlc_rwlock_unlock (vlc_rwlock_t *lock) +static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock) { vlc_mutex_lock (&lock->mutex); - if (lock->readers > 0) - lock->readers--; /* Read unlock */ - else - lock->writer = 0; /* Write unlock */ + assert (lock->writer == GetCurrentThreadId ()); + assert (lock->readers == 0); + lock->writer = 0; /* Write unlock */ + /* Let reader and writer compete. Scheduler decides who wins. */ if (lock->writers > 0) - { - if (lock->readers == 0) - vlc_cond_signal (&lock->write_wait); - } - else - vlc_cond_broadcast (&lock->read_wait); + vlc_cond_signal (&lock->write_wait); + vlc_cond_broadcast (&lock->read_wait); vlc_mutex_unlock (&lock->mutex); } +void vlc_rwlock_unlock (vlc_rwlock_t *lock) +{ + /* Note: If the lock is held for reading, lock->writer is nul. + * If the lock is held for writing, only this thread can store a value to + * lock->writer. Either way, lock->writer is safe to fetch here. */ + if (lock->writer != 0) + vlc_rwlock_wrunlock (lock); + else + vlc_rwlock_rdunlock (lock); +} + /*** Thread-specific variables (TLS) ***/ +struct vlc_threadvar +{ + DWORD id; + void (*destroy) (void *); + struct vlc_threadvar *prev; + struct vlc_threadvar *next; +} *vlc_threadvar_last = NULL; + int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *)) { -#warning FIXME: use destr() callback and stop leaking! - VLC_UNUSED( destr ); + struct vlc_threadvar *var = malloc (sizeof (*var)); + if (unlikely(var == NULL)) + return errno; - *p_tls = TlsAlloc(); - return (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0; + var->id = TlsAlloc(); + if (var->id == TLS_OUT_OF_INDEXES) + { + free (var); + return EAGAIN; + } + var->destroy = destr; + var->next = NULL; + *p_tls = var; + + EnterCriticalSection (&super_mutex); + var->prev = vlc_threadvar_last; + vlc_threadvar_last = var; + LeaveCriticalSection (&super_mutex); + return 0; } void vlc_threadvar_delete (vlc_threadvar_t *p_tls) { - TlsFree (*p_tls); + struct vlc_threadvar *var = *p_tls; + + EnterCriticalSection (&super_mutex); + if (var->prev != NULL) + var->prev->next = var->next; + else + vlc_threadvar_last = var->next; + if (var->next != NULL) + var->next->prev = var->prev; + LeaveCriticalSection (&super_mutex); + + TlsFree (var->id); + free (var); } -/** - * Sets a thread-local variable. - * @param key thread-local variable key (created with vlc_threadvar_create()) - * @param value new value for the variable for the calling thread - * @return 0 on success, a system error code otherwise. - */ int vlc_threadvar_set (vlc_threadvar_t key, void *value) { - return TlsSetValue (key, value) ? ENOMEM : 0; + return TlsSetValue (key->id, value) ? ENOMEM : 0; } -/** - * Gets the value of a thread-local variable for the calling thread. - * This function cannot fail. - * @return the value associated with the given variable for the calling - * or NULL if there is no value. - */ void *vlc_threadvar_get (vlc_threadvar_t key) { - return TlsGetValue (key); + return TlsGetValue (key->id); } - /*** Threads ***/ void vlc_threads_setup (libvlc_int_t *p_libvlc) { (void) p_libvlc; } -struct vlc_entry_data +static void vlc_thread_cleanup (struct vlc_thread *th) { - void * (*func) (void *); - void * data; + vlc_threadvar_t key; + +retry: + /* TODO: use RW lock or something similar */ + EnterCriticalSection (&super_mutex); + for (key = vlc_threadvar_last; key != NULL; key = key->prev) + { + void *value = vlc_threadvar_get (key); + if (value != NULL && key->destroy != NULL) + { + EnterCriticalSection (&super_mutex); + vlc_threadvar_set (key, NULL); + key->destroy (value); + goto retry; + } + } + EnterCriticalSection (&super_mutex); + + if (th->detached) + { + CloseHandle (th->id); #ifdef UNDER_CE - HANDLE cancel_event; + CloseHandle (th->cancel_event); #endif -}; + free (th); + } +} static unsigned __stdcall vlc_entry (void *p) { - vlc_cancel_t cancel_data = VLC_CANCEL_INIT; - struct vlc_entry_data data; + struct vlc_thread *th = p; - memcpy (&data, p, sizeof (data)); - free (p); - -#ifdef UNDER_CE - cancel_data.cancel_event = data.cancel_event; -#endif - - vlc_threadvar_set (cancel_key, &cancel_data); - data.func (data.data); + vlc_threadvar_set (thread_key, th); + th->killable = true; + th->data = th->entry (th->data); + vlc_thread_cleanup (th); return 0; } -int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, - int priority) +static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached, + void *(*entry) (void *), void *data, int priority) { - int err = ENOMEM; - HANDLE hThread; - - struct vlc_entry_data *entry_data = malloc (sizeof (*entry_data)); - if (entry_data == NULL) + struct vlc_thread *th = malloc (sizeof (*th)); + if (unlikely(th == NULL)) return ENOMEM; - entry_data->func = entry; - entry_data->data = data; + th->entry = entry; + th->data = data; + th->detached = detached; + th->killable = false; /* not until vlc_entry() ! */ + th->killed = false; + th->cleaners = NULL; + HANDLE hThread; #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; - } + uintptr_t h; - /* 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)) + h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL); + if (h == 0) { - CloseHandle (hThread); - goto error; + int err = errno; + free (th); + return err; } + hThread = (HANDLE)h; #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 (th); - goto error; + return ENOMEM; } - entry_data->cancel_event = th->cancel_event; /* 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) + hThread = CreateThread (NULL, 128*1024, vlc_entry, th, + CREATE_SUSPENDED, NULL); + if (hThread == NULL) { CloseHandle (th->cancel_event); free (th); - goto error; + return ENOMEM; } - *p_handle = th; - hThread = th->handle; - #endif + /* Thread is suspended, so we can safely set th->id */ + th->id = hThread; + if (p_handle != NULL) + *p_handle = th; - ResumeThread (hThread); if (priority) SetThreadPriority (hThread, priority); + ResumeThread (hThread); + return 0; +} -error: - free (entry_data); - return err; +int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *), + void *data, int priority) +{ + return vlc_clone_attr (p_handle, false, entry, data, priority); } -void vlc_join (vlc_thread_t handle, void **result) +void vlc_join (vlc_thread_t th, void **result) { -#ifdef UNDER_CE -# define handle handle->handle -#endif do vlc_testcancel (); - while (WaitForSingleObjectEx (handle, INFINITE, TRUE) + while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION); - CloseHandle (handle); - assert (result == NULL); /* <- FIXME if ever needed */ + if (result != NULL) + *result = th->data; + CloseHandle (th->id); #ifdef UNDER_CE -# undef handle - CloseHandle (handle->cancel_event); - free (handle); + CloseHandle (th->cancel_event); #endif + free (th); } -void vlc_detach (vlc_thread_t handle) +int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *), + void *data, int priority) { -#ifndef UNDER_CE - CloseHandle (handle); -#else - /* FIXME: handle->cancel_event leak */ - CloseHandle (handle->handle); - free (handle); -#endif + vlc_thread_t th; + if (p_handle == NULL) + p_handle = &th; + + return vlc_clone_attr (p_handle, true, entry, data, priority); +} + +int vlc_set_priority (vlc_thread_t th, int priority) +{ + if (!SetThreadPriority (th->id, priority)) + return VLC_EGENERIC; + return VLC_SUCCESS; } /*** Thread cancellation ***/ /* APC procedure for thread cancellation */ -static void CALLBACK vlc_cancel_self (ULONG_PTR dummy) +static void CALLBACK vlc_cancel_self (ULONG_PTR self) { - (void)dummy; - vlc_control_cancel (VLC_DO_CANCEL); + struct vlc_thread *th = (void *)self; + + if (likely(th != NULL)) + th->killed = true; } -void vlc_cancel (vlc_thread_t thread_id) +void vlc_cancel (vlc_thread_t th) { #ifndef UNDER_CE - QueueUserAPC (vlc_cancel_self, thread_id, 0); + QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th); #else - SetEvent (thread_id->cancel_event); + SetEvent (th->cancel_event); #endif } int vlc_savecancel (void) { - int state; - - vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key); - if (nfo == NULL) + struct vlc_thread *th = vlc_threadvar_get (thread_key); + if (th == NULL) return false; /* Main thread - cannot be cancelled anyway */ - state = nfo->killable; - nfo->killable = false; + int state = th->killable; + th->killable = false; return state; } void vlc_restorecancel (int state) { - vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key); + struct vlc_thread *th = vlc_threadvar_get (thread_key); assert (state == false || state == true); - if (nfo == NULL) + if (th == NULL) return; /* Main thread - cannot be cancelled anyway */ - assert (!nfo->killable); - nfo->killable = state != 0; + assert (!th->killable); + th->killable = state != 0; } void vlc_testcancel (void) { - vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key); - if (nfo == NULL) + struct vlc_thread *th = vlc_threadvar_get (thread_key); + if (th == NULL) return; /* Main thread - cannot be cancelled anyway */ - if (nfo->killable && nfo->killed) + if (th->killable && th->killed) { - for (vlc_cleanup_t *p = nfo->cleaners; p != NULL; p = p->next) + for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next) p->proc (p->data); + + th->data = NULL; /* TODO: special value? */ + vlc_thread_cleanup (th); #ifndef UNDER_CE - _endthread (); + _endthreadex(0); #else ExitThread(0); #endif @@ -696,30 +745,26 @@ void vlc_control_cancel (int cmd, ...) * need to lock anything. */ va_list ap; - vlc_cancel_t *nfo = vlc_threadvar_get (cancel_key); - if (nfo == NULL) + struct vlc_thread *th = vlc_threadvar_get (thread_key); + if (th == NULL) return; /* Main thread - cannot be cancelled anyway */ va_start (ap, cmd); switch (cmd) { - case VLC_DO_CANCEL: - nfo->killed = true; - break; - case VLC_CLEANUP_PUSH: { /* cleaner is a pointer to the caller stack, no need to allocate * and copy anything. As a nice side effect, this cannot fail. */ vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *); - cleaner->next = nfo->cleaners; - nfo->cleaners = cleaner; + cleaner->next = th->cleaners; + th->cleaners = cleaner; break; } case VLC_CLEANUP_POP: { - nfo->cleaners = nfo->cleaners->next; + th->cleaners = th->cleaners->next; break; } } @@ -727,6 +772,45 @@ void vlc_control_cancel (int cmd, ...) } +/*** Clock ***/ +mtime_t mdate (void) +{ + /* We don't need the real date, just the value of a high precision timer */ + LARGE_INTEGER counter, freq; + if (!QueryPerformanceCounter (&counter) + || !QueryPerformanceFrequency (&freq)) + abort(); + + /* Convert to from (1/freq) to microsecond resolution */ + /* We need to split the division to avoid 63-bits overflow */ + lldiv_t d = lldiv (counter.QuadPart, freq.QuadPart); + + return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart); +} + +#undef mwait +void mwait (mtime_t deadline) +{ + mtime_t delay; + + vlc_testcancel(); + while ((delay = (deadline - mdate())) > 0) + { + delay /= 1000; + if (unlikely(delay > 0x7fffffff)) + delay = 0x7fffffff; + vlc_Sleep (delay); + vlc_testcancel(); + } +} + +#undef msleep +void msleep (mtime_t delay) +{ + mwait (mdate () + delay); +} + + /*** Timers ***/ struct vlc_timer { @@ -855,3 +939,17 @@ unsigned vlc_timer_getoverrun (vlc_timer_t timer) (void)timer; return 0; } + + +/*** CPU ***/ +unsigned vlc_GetCPUCount (void) +{ +#ifndef UNDER_CE + DWORD process; + DWORD system; + + if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system)) + return popcount (system); +#endif + return 1; +}