]> git.sesse.net Git - vlc/blobdiff - src/win32/thread.c
Initialize the configuration R/W lock statically
[vlc] / src / win32 / thread.c
index 2cd374fc3c6f011e36be5c85aad9ef090b375434..965db9906e05d3ebbb66ad1ad61515c8dbf095ac 100644 (file)
@@ -39,6 +39,7 @@
 #ifdef UNDER_CE
 # include <mmsystem.h>
 #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);
 
-static vlc_mutex_t super_mutex;
-static 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);
 }
@@ -500,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;
 }
 
@@ -511,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);
@@ -546,19 +538,19 @@ static void vlc_thread_cleanup (struct vlc_thread *th)
 
 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);
 
     if (th->detached)
     {
@@ -600,14 +592,16 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
      * 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
     th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
@@ -652,7 +646,7 @@ 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)
@@ -805,7 +799,7 @@ void mwait (mtime_t deadline)
         delay /= 1000;
         if (unlikely(delay > 0x7fffffff))
             delay = 0x7fffffff;
-        SleepEx (delay, TRUE);
+        vlc_Sleep (delay);
         vlc_testcancel();
     }
 }