X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fwin32%2Fthread.c;h=965db9906e05d3ebbb66ad1ad61515c8dbf095ac;hb=236a276618fc83c18e100ace359c51ba4734010c;hp=365025674f25072070903c743357ef25f37b9820;hpb=6bc9535c0a97e1ad3f8531ffd1dcbb5d565656ab;p=vlc diff --git a/src/win32/thread.c b/src/win32/thread.c index 365025674f..965db9906e 100644 --- a/src/win32/thread.c +++ b/src/win32/thread.c @@ -39,6 +39,7 @@ #ifdef UNDER_CE # include #endif +#include "config/configuration.h" static vlc_threadvar_t thread_key; @@ -61,110 +62,97 @@ struct vlc_thread 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) { - struct vlc_thread *th = vlc_threadvar_get (thread_key); - if (th == 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] = th->cancel_event; - DWORD result = WaitForMultipleObjects (count + 1, new_handles, FALSE, - delay); - if (result == WAIT_OBJECT_0 + count) - { - vlc_cancel_self ((uintptr_t)th); - 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 (&thread_key, NULL); - break; + return vlc_WaitForMultipleObjects (1, &handle, delay); +} - case DLL_PROCESS_DETACH: - vlc_threadvar_delete (&thread_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,20 +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 */ - int canc = vlc_savecancel (); - 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); - vlc_restorecancel (canc); + LeaveCriticalSection (&super_mutex); return; } @@ -216,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; } @@ -234,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; } @@ -251,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) @@ -281,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); } @@ -298,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); } @@ -318,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 (); @@ -326,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; @@ -379,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); } @@ -392,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); @@ -406,57 +395,78 @@ 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 { @@ -482,10 +492,10 @@ int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *)) var->next = NULL; *p_tls = var; - vlc_mutex_lock (&super_mutex); + EnterCriticalSection (&super_mutex); var->prev = vlc_threadvar_last; vlc_threadvar_last = var; - vlc_mutex_unlock (&super_mutex); + LeaveCriticalSection (&super_mutex); return 0; } @@ -493,14 +503,14 @@ void vlc_threadvar_delete (vlc_threadvar_t *p_tls) { struct vlc_threadvar *var = *p_tls; - vlc_mutex_lock (&super_mutex); + 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; - vlc_mutex_unlock (&super_mutex); + LeaveCriticalSection (&super_mutex); TlsFree (var->id); free (var); @@ -516,32 +526,40 @@ void *vlc_threadvar_get (vlc_threadvar_t key) return TlsGetValue (key->id); } -static void vlc_threadvar_cleanup (void) +/*** Threads ***/ +void vlc_threads_setup (libvlc_int_t *p_libvlc) +{ + (void) p_libvlc; +} + +static void vlc_thread_cleanup (struct vlc_thread *th) { vlc_threadvar_t key; retry: /* TODO: use RW lock or something similar */ - vlc_mutex_lock (&super_mutex); + 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) { - vlc_mutex_unlock (&super_mutex); + EnterCriticalSection (&super_mutex); vlc_threadvar_set (key, NULL); key->destroy (value); goto retry; } } - vlc_mutex_unlock (&super_mutex); -} - + EnterCriticalSection (&super_mutex); -/*** Threads ***/ -void vlc_threads_setup (libvlc_int_t *p_libvlc) -{ - (void) p_libvlc; + if (th->detached) + { + CloseHandle (th->id); +#ifdef UNDER_CE + CloseHandle (th->cancel_event); +#endif + free (th); + } } static unsigned __stdcall vlc_entry (void *p) @@ -551,21 +569,19 @@ static unsigned __stdcall vlc_entry (void *p) vlc_threadvar_set (thread_key, th); th->killable = true; th->data = th->entry (th->data); - vlc_threadvar_cleanup (); - if (th->detached) - free (th); + 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) { struct vlc_thread *th = malloc (sizeof (*th)); if (unlikely(th == NULL)) return ENOMEM; th->entry = entry; th->data = data; - th->detached = p_handle == NULL; + th->detached = detached; th->killable = false; /* not until vlc_entry() ! */ th->killed = false; th->cleaners = NULL; @@ -576,17 +592,18 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, * 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, th, CREATE_SUSPENDED, NULL); - if (hThread == NULL) + uintptr_t h; + + h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL); + if (h == 0) { int err = errno; free (th); return err; } + hThread = (HANDLE)h; #else - /* FIXME: cancel_event is useless and leaked in detached threads */ th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL); if (th->cancel_event == NULL) { @@ -606,39 +623,56 @@ int vlc_clone (vlc_thread_t *p_handle, void * (*entry) (void *), void *data, } #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); - if (p_handle != NULL) - *p_handle = th; - else - CloseHandle (hThread); + ResumeThread (hThread); return 0; } +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 th, void **result) { do vlc_testcancel (); - while (WaitForSingleObjectEx (th->id, INFINITE, TRUE) + while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION); + if (result != NULL) + *result = th->data; CloseHandle (th->id); #ifdef UNDER_CE CloseHandle (th->cancel_event); #endif - if (result != NULL) - *result = th->data; free (th); } -int vlc_clone_detach (void *(*entry) (void *), void *data, int priority) +int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *), + void *data, int priority) +{ + 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) { - return vlc_clone (NULL, entry, data, priority); + if (!SetThreadPriority (th->id, priority)) + return VLC_EGENERIC; + return VLC_SUCCESS; } /*** Thread cancellation ***/ @@ -692,14 +726,11 @@ void vlc_testcancel (void) if (th->killable && th->killed) { - /* Detached threads cannot be cancelled */ - assert (!th->detached); - - th->data = NULL; /* TODO: special value? */ - for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next) p->proc (p->data); - vlc_threadvar_cleanup (); + + th->data = NULL; /* TODO: special value? */ + vlc_thread_cleanup (th); #ifndef UNDER_CE _endthreadex(0); #else @@ -741,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 { @@ -869,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; +}