[vlc-commits] win32: run-time fallback for WaitOnAddress() and WakeByAddress()

Rémi Denis-Courmont git at videolan.org
Fri May 27 23:43:03 CEST 2016


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Thu May 26 22:32:21 2016 +0300| [58191d7d7dbbc39b0f0c44bb8b6070d49597097c] | committer: Rémi Denis-Courmont

win32: run-time fallback for WaitOnAddress() and WakeByAddress()

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=58191d7d7dbbc39b0f0c44bb8b6070d49597097c
---

 src/win32/thread.c |  135 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/src/win32/thread.c b/src/win32/thread.c
index 676ff48..4920366 100644
--- a/src/win32/thread.c
+++ b/src/win32/thread.c
@@ -440,9 +440,116 @@ retry:
     vlc_mutex_unlock(&super_mutex);
 }
 
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
 /*** Futeces^WAddress waits ***/
+#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
+static BOOL (WINAPI *WaitOnAddress_)(VOID volatile *, PVOID, SIZE_T, DWORD);
+#define WaitOnAddress (*WaitOnAddress_)
+static VOID (WINAPI *WakeByAddressAll_)(PVOID);
+#define WakeByAddressAll (*WakeByAddressAll_)
+static VOID (WINAPI *WakeByAddressSingle_)(PVOID);
+#define WakeByAddressSingle (*WakeByAddressSingle_)
+
+static struct wait_addr_bucket
+{
+    CRITICAL_SECTION lock;
+    CONDITION_VARIABLE wait;
+} wait_addr_buckets[32];
+
+static struct wait_addr_bucket *wait_addr_get_bucket(void volatile *addr)
+{
+    uintptr_t u = (uintptr_t)addr;
+
+    return wait_addr_buckets + ((u >> 3) % ARRAY_SIZE(wait_addr_buckets));
+}
+
+static void vlc_wait_addr_init(void)
+{
+    for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
+    {
+        struct wait_addr_bucket *bucket = wait_addr_buckets + i;
+
+        InitializeCriticalSection(&bucket->lock);
+        InitializeConditionVariable(&bucket->wait);
+    }
+}
+
+static void vlc_wait_addr_deinit(void)
+{
+    for (size_t i = 0; i < ARRAY_SIZE(wait_addr_buckets); i++)
+    {
+        struct wait_addr_bucket *bucket = wait_addr_buckets + i;
+
+        DeleteCriticalSection(&bucket->lock);
+    }
+}
+
+static BOOL WINAPI WaitOnAddressFallback(void volatile *addr, void *value,
+                                         SIZE_T size, DWORD ms)
+{
+    struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
+    uint64_t futex, val = 0;
+    BOOL ret = 0;
+
+    EnterCriticalSection(&bucket->lock);
+
+    switch (size)
+    {
+        case 1:
+            futex = atomic_load_explicit((atomic_char *)addr,
+                                         memory_order_relaxed);
+            val = *(const char *)value;
+            break;
+        case 2:
+            futex = atomic_load_explicit((atomic_short *)addr,
+                                         memory_order_relaxed);
+            val = *(const short *)value;
+            break;
+        case 4:
+            futex = atomic_load_explicit((atomic_int *)addr,
+                                         memory_order_relaxed);
+            val = *(const int *)value;
+            break;
+        case 8:
+            futex = atomic_load_explicit((atomic_llong *)addr,
+                                         memory_order_relaxed);
+            val = *(const long long *)value;
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+
+    if (futex == val)
+        ret = SleepConditionVariableCS(&bucket->wait, &bucket->lock, ms);
+
+    LeaveCriticalSection(&bucket->lock);
+    return ret;
+}
+
+static void WINAPI WakeByAddressFallback(void *addr)
+{
+    struct wait_addr_bucket *bucket = wait_addr_get_bucket(addr);
+
+    /* Acquire the bucket critical section (only) to enforce proper sequencing.
+     * The critical section does not protect any actual memory object. */
+    EnterCriticalSection(&bucket->lock);
+    /* No other threads can hold the lock for this bucket while it is held
+     * here. Thus any other thread either:
+     * - is already sleeping in SleepConditionVariableCS(), and to be woken up
+     *   by the following WakeAllConditionVariable(), or
+     * - has yet to retrieve the value at the wait address (with the
+     *   'switch (size)' block). */
+    LeaveCriticalSection(&bucket->lock);
+    /* At this point, other threads can retrieve the value at the wait address.
+     * But the value will have already been changed by our call site, thus
+     * (futex == val) will be false, and the threads will not go to sleep. */
+
+    /* Wake up any thread that was already sleeping. Since there are more than
+     * one wait address per bucket, all threads must be woken up :-/ */
+    WakeAllConditionVariable(&bucket->wait);
+}
+#endif
 
-#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
 void vlc_addr_wait(void *addr, int val)
 {
     WaitOnAddress(addr, &val, sizeof (val), -1);
@@ -1041,6 +1148,8 @@ void vlc_threads_setup (libvlc_int_t *p_libvlc)
     SelectClockSource (VLC_OBJECT(p_libvlc));
 }
 
+#define LOOKUP(s) (((s##_) = (void *)GetProcAddress(h, #s)) != NULL)
+
 extern vlc_rwlock_t config_lock;
 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
 
@@ -1052,6 +1161,23 @@ BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
     switch (fdwReason)
     {
         case DLL_PROCESS_ATTACH:
+        {
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
+            HANDLE h = GetModuleHandle(TEXT("kernel32.dll"));
+            if (unlikely(h == NULL))
+                return FALSE;
+
+            if (!LOOKUP(WaitOnAddress)
+             || !LOOKUP(WakeByAddressAll) || !LOOKUP(WakeByAddressSingle))
+            {
+                vlc_wait_addr_init();
+                WaitOnAddress_ = WaitOnAddressFallback;
+                WakeByAddressAll_ = WakeByAddressFallback;
+                WakeByAddressSingle_ = WakeByAddressFallback;
+            }
+#endif
+#endif
             thread_key = TlsAlloc();
             if (unlikely(thread_key == TLS_OUT_OF_INDEXES))
                 return FALSE;
@@ -1061,6 +1187,7 @@ BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
             vlc_rwlock_init (&config_lock);
             vlc_CPU_init ();
             break;
+        }
 
         case DLL_PROCESS_DETACH:
             vlc_rwlock_destroy (&config_lock);
@@ -1068,6 +1195,12 @@ BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
             vlc_mutex_destroy (&super_mutex);
             DeleteCriticalSection (&clock_lock);
             TlsFree(thread_key);
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
+            if (WaitOnAddress_ == WaitOnAddressFallback)
+                vlc_wait_addr_deinit();
+#endif
+#endif
             break;
 
         case DLL_THREAD_DETACH:



More information about the vlc-commits mailing list