]> git.sesse.net Git - vlc/blobdiff - src/win32/thread.c
rwlock: reduce footprint and factor Win32 and OS/2 back-ends
[vlc] / src / win32 / thread.c
index ebaaec8d9a841943123aae8edb00fb01d1473c3d..848c5a3c68c0ee31d22998748e5737594c126fae 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * thread.c : Win32 back-end for LibVLC
  *****************************************************************************
- * Copyright (C) 1999-2009 the VideoLAN team
+ * Copyright (C) 1999-2009 VLC authors and VideoLAN
  *
  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
  *          Samuel Hocevar <sam@zoy.org>
  *          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
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #include <assert.h>
 #include <limits.h>
 #include <errno.h>
-#ifdef UNDER_CE
-# include <mmsystem.h>
-#endif
-
-static vlc_threadvar_t thread_key;
-
-/**
- * Per-thread data
- */
-struct vlc_thread
-{
-    HANDLE         id;
-#ifdef UNDER_CE
-    HANDLE         cancel_event;
-#endif
-
-    bool           detached;
-    bool           killable;
-    bool           killed;
-    vlc_cleanup_t *cleaners;
-
-    void        *(*entry) (void *);
-    void          *data;
-};
-
-#ifdef UNDER_CE
-static void CALLBACK vlc_cancel_self (ULONG_PTR dummy);
-
-DWORD WaitForMultipleObjectsEx (DWORD nCount, const HANDLE *lpHandles,
-                                BOOL bWaitAll, DWORD dwMilliseconds,
-                                BOOL bAlertable)
-{
-    struct vlc_thread *th = vlc_threadvar_get (thread_key);
-    HANDLE handles[nCount + 1];
-    DWORD ret;
-
-    memcpy(handles, lpHandles, nCount * sizeof(HANDLE));
-    if (bAlertable)
-    {
-        if (th != NULL)
-        {
-            handles[nCount] = th->cancel_event;
-            /* bWaitAll not implemented and not used by VLC... */
-            assert (!bWaitAll);
-        }
-        else
-            bAlertable = FALSE;
-    }
-
-    ret = WaitForMultipleObjects (nCount + bAlertable, handles, bWaitAll,
-                                  dwMilliseconds);
-    if (ret == WAIT_OBJECT_0 + nCount)
-    {
-        assert (bAlertable);
-        vlc_cancel_self ((uintptr_t)th);
-        ret = WAIT_IO_COMPLETION;
-    }
-    return ret;
-}
-#endif
 
+/*** Static mutex and condition variable ***/
 static vlc_mutex_t super_mutex;
 static vlc_cond_t  super_variable;
 
-BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
-
-BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
-{
-    (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;
-
-        case DLL_PROCESS_DETACH:
-            vlc_threadvar_delete (&thread_key);
-            vlc_cond_destroy (&super_variable);
-            vlc_mutex_destroy (&super_mutex);
-            break;
-    }
-    return TRUE;
-}
 
+/*** Common helpers ***/
 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
                                          DWORD delay)
 {
-    DWORD ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
-
-   /* We do not abandon objects... this would be a bug */
-   assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
+    DWORD ret;
+    if (count == 0)
+    {
+        ret = SleepEx (delay, TRUE);
+        if (ret == 0)
+            ret = WAIT_TIMEOUT;
+    }
+    else
+        ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
 
-   if (unlikely(ret == WAIT_FAILED))
-       abort (); /* We are screwed! */
+    /* We do not abandon objects... this would be a bug */
+    assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
 
-   return ret;
+    if (unlikely(ret == WAIT_FAILED))
+        abort (); /* We are screwed! */
+    return ret;
 }
 
 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
@@ -143,15 +69,11 @@ static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
     return vlc_WaitForMultipleObjects (1, &handle, delay);
 }
 
-#if 0 // WaitForMultipleObjectsEx() cannot deal with zero handles
 static DWORD vlc_Sleep (DWORD delay)
 {
     DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
-    if (ret == WAIT_TIMEOUT)
-        ret = 0;
-    return ret;
+    return (ret != WAIT_TIMEOUT) ? ret : 0;
 }
-#endif
 
 
 /*** Mutexes ***/
@@ -240,6 +162,7 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
 /*** Condition variables ***/
 enum
 {
+    CLOCK_STATIC=0, /* must be zero for VLC_STATIC_COND */
     CLOCK_MONOTONIC,
     CLOCK_REALTIME,
 };
@@ -270,16 +193,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->clock)
+        return;
+
+    /* This is suboptimal but works. */
+    vlc_cond_broadcast (p_condvar);
 }
 
 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
 {
+    if (!p_condvar->clock)
+        return;
+
+    /* Wake all threads up (as the event HANDLE has manual reset) */
     SetEvent (p_condvar->handle);
 }
 
@@ -287,13 +213,18 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
 {
     DWORD result;
 
-    assert (p_mutex->dynamic); /* TODO */
+    if (!p_condvar->clock)
+    {   /* FIXME FIXME FIXME */
+        msleep (50000);
+        return;
+    }
+
     do
     {
         vlc_testcancel ();
-        LeaveCriticalSection (&p_mutex->mutex);
+        vlc_mutex_unlock (p_mutex);
         result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
-        EnterCriticalSection (&p_mutex->mutex);
+        vlc_mutex_lock (p_mutex);
     }
     while (result == WAIT_IO_COMPLETION);
 
@@ -305,7 +236,6 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
 {
     DWORD result;
 
-    assert (p_mutex->dynamic); /* TODO */
     do
     {
         vlc_testcancel ();
@@ -313,22 +243,26 @@ 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 (p_condvar->clock == CLOCK_MONOTONIC);
-                total = mdate();
-                break;
+                assert (!p_condvar->clock);
+                /* FIXME FIXME FIXME */
+                msleep (50000);
+                return 0;
         }
         total = (deadline - total) / 1000;
         if( total < 0 )
             total = 0;
 
         DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
-        LeaveCriticalSection (&p_mutex->mutex);
+        vlc_mutex_unlock (p_mutex);
         result = vlc_WaitForSingleObject (p_condvar->handle, delay);
-        EnterCriticalSection (&p_mutex->mutex);
+        vlc_mutex_lock (p_mutex);
     }
     while (result == WAIT_IO_COMPLETION);
 
@@ -369,95 +303,30 @@ void vlc_sem_wait (vlc_sem_t *sem)
 }
 
 /*** Read/write locks */
+#if 0
 /* SRW (Slim Read Write) locks are available in Vista+ only */
 void vlc_rwlock_init (vlc_rwlock_t *lock)
 {
-    vlc_mutex_init (&lock->mutex);
-    vlc_cond_init (&lock->read_wait);
-    vlc_cond_init (&lock->write_wait);
-    lock->readers = 0; /* active readers */
-    lock->writers = 0; /* waiting writers */
-    lock->writer = 0; /* ID of active writer */
 }
 
 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
 {
-    vlc_cond_destroy (&lock->read_wait);
-    vlc_cond_destroy (&lock->write_wait);
-    vlc_mutex_destroy (&lock->mutex);
 }
 
 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 (unlikely(lock->readers == ULONG_MAX))
-        abort ();
-    lock->readers++;
-    vlc_mutex_unlock (&lock->mutex);
-}
-
-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 (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);
-}
-
-static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
-{
-    vlc_mutex_lock (&lock->mutex);
-    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)
-        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);
 }
+#else
+# include "misc/rwlock.h"
+#endif
 
 /*** Thread-specific variables (TLS) ***/
 struct vlc_threadvar
@@ -486,6 +355,9 @@ int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
 
     vlc_mutex_lock (&super_mutex);
     var->prev = vlc_threadvar_last;
+    if (var->prev)
+        var->prev->next = var;
+
     vlc_threadvar_last = var;
     vlc_mutex_unlock (&super_mutex);
     return 0;
@@ -498,10 +370,12 @@ void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
     vlc_mutex_lock (&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;
+    else
+        vlc_threadvar_last = var->prev;
+
     vlc_mutex_unlock (&super_mutex);
 
     TlsFree (var->id);
@@ -519,10 +393,21 @@ void *vlc_threadvar_get (vlc_threadvar_t key)
 }
 
 /*** Threads ***/
-void vlc_threads_setup (libvlc_int_t *p_libvlc)
+static vlc_threadvar_t thread_key;
+
+/** Per-thread data */
+struct vlc_thread
 {
-    (void) p_libvlc;
-}
+    HANDLE         id;
+
+    bool           detached;
+    bool           killable;
+    bool           killed;
+    vlc_cleanup_t *cleaners;
+
+    void        *(*entry) (void *);
+    void          *data;
+};
 
 static void vlc_thread_cleanup (struct vlc_thread *th)
 {
@@ -547,9 +432,6 @@ retry:
     if (th->detached)
     {
         CloseHandle (th->id);
-#ifdef UNDER_CE
-        CloseHandle (th->cancel_event);
-#endif
         free (th);
     }
 }
@@ -579,7 +461,6 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
     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
@@ -595,26 +476,6 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
     }
     hThread = (HANDLE)h;
 
-#else
-    th->cancel_event = CreateEvent (NULL, FALSE, FALSE, NULL);
-    if (th->cancel_event == NULL)
-    {
-        free (th);
-        return ENOMEM;
-    }
-
-    /* Not sure if CREATE_SUSPENDED + ResumeThread() is any useful on WinCE.
-     * Thread handles act up, too. */
-    hThread = CreateThread (NULL, 128*1024, vlc_entry, th,
-                            CREATE_SUSPENDED, NULL);
-    if (hThread == NULL)
-    {
-        CloseHandle (th->cancel_event);
-        free (th);
-        return ENOMEM;
-    }
-
-#endif
     /* Thread is suspended, so we can safely set th->id */
     th->id = hThread;
     if (p_handle != NULL)
@@ -638,15 +499,11 @@ void vlc_join (vlc_thread_t th, void **result)
 {
     do
         vlc_testcancel ();
-    while (WaitForSingleObjectEx (th->id, INFINITE, TRUE)
-                                                        == WAIT_IO_COMPLETION);
+    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
     free (th);
 }
 
@@ -680,11 +537,7 @@ static void CALLBACK vlc_cancel_self (ULONG_PTR self)
 
 void vlc_cancel (vlc_thread_t th)
 {
-#ifndef UNDER_CE
     QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
-#else
-    SetEvent (th->cancel_event);
-#endif
 }
 
 int vlc_savecancel (void)
@@ -723,11 +576,7 @@ void vlc_testcancel (void)
 
         th->data = NULL; /* TODO: special value? */
         vlc_thread_cleanup (th);
-#ifndef UNDER_CE
         _endthreadex(0);
-#else
-        ExitThread(0);
-#endif
     }
 }
 
@@ -763,21 +612,110 @@ void vlc_control_cancel (int cmd, ...)
     va_end (ap);
 }
 
-
 /*** Clock ***/
+static CRITICAL_SECTION clock_lock;
+
+static mtime_t mdate_giveup (void)
+{
+    abort ();
+}
+
+static mtime_t (*mdate_selected) (void) = mdate_giveup;
+
 mtime_t mdate (void)
+{
+    return mdate_selected ();
+}
+
+static union
+{
+    struct
+    {
+#if (_WIN32_WINNT < 0x0601)
+        BOOL (*query) (PULONGLONG);
+#endif
+    } interrupt;
+    struct
+    {
+#if (_WIN32_WINNT < 0x0600)
+        ULONGLONG (*get) (void);
+#endif
+    } tick;
+    struct
+    {
+        LARGE_INTEGER freq;
+    } perf;
+} clk;
+
+static mtime_t mdate_interrupt (void)
+{
+    ULONGLONG ts;
+    BOOL ret;
+
+#if (_WIN32_WINNT >= 0x0601)
+    ret = QueryUnbiasedInterruptTime (&ts);
+#else
+    ret = clk.interrupt.query (&ts);
+#endif
+    if (unlikely(!ret))
+        abort ();
+
+    /* hundreds of nanoseconds */
+    static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
+    return ts / (10000000 / CLOCK_FREQ);
+}
+
+static mtime_t mdate_tick (void)
+{
+#if (_WIN32_WINNT >= 0x0600)
+    ULONGLONG ts = GetTickCount64 ();
+#else
+    ULONGLONG ts = clk.tick.get ();
+#endif
+
+    /* milliseconds */
+    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
+    return ts * (CLOCK_FREQ / 1000);
+}
+#include <mmsystem.h>
+static mtime_t mdate_multimedia (void)
+{
+     DWORD ts = timeGetTime ();
+
+    /* milliseconds */
+    static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
+    return ts * (CLOCK_FREQ / 1000);
+}
+
+static mtime_t mdate_perf (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();
+    LARGE_INTEGER counter;
+    if (!QueryPerformanceCounter (&counter))
+        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);
+    lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
 
-    return (d.quot * 1000000) + ((d.rem * 1000000) / freq.QuadPart);
+    return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
+}
+
+static mtime_t mdate_wall (void)
+{
+    FILETIME ts;
+    ULARGE_INTEGER s;
+
+#if (_WIN32_WINNT >= 0x0602)
+    GetSystemTimePreciseAsFileTime (&ts);
+#else
+    GetSystemTimeAsFileTime (&ts);
+#endif
+    s.LowPart = ts.dwLowDateTime;
+    s.HighPart = ts.dwHighDateTime;
+    /* hundreds of nanoseconds */
+    static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
+    return s.QuadPart / (10000000 / CLOCK_FREQ);
 }
 
 #undef mwait
@@ -791,7 +729,7 @@ void mwait (mtime_t deadline)
         delay /= 1000;
         if (unlikely(delay > 0x7fffffff))
             delay = 0x7fffffff;
-        SleepEx (delay, TRUE);
+        vlc_Sleep (delay);
         vlc_testcancel();
     }
 }
@@ -802,21 +740,141 @@ void msleep (mtime_t delay)
     mwait (mdate () + delay);
 }
 
+static void SelectClockSource (vlc_object_t *obj)
+{
+    EnterCriticalSection (&clock_lock);
+    if (mdate_selected != mdate_giveup)
+    {
+        LeaveCriticalSection (&clock_lock);
+        return;
+    }
+
+    const char *name = "perf";
+    char *str = var_InheritString (obj, "clock-source");
+    if (str != NULL)
+        name = str;
+    if (!strcmp (name, "interrupt"))
+    {
+        msg_Dbg (obj, "using interrupt time as clock source");
+#if (_WIN32_WINNT < 0x0601)
+        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
+        if (unlikely(h == NULL))
+            abort ();
+        clk.interrupt.query = (void *)GetProcAddress (h,
+                                                      _T("QueryUnbiasedInterruptTime"));
+        if (unlikely(clk.interrupt.query == NULL))
+            abort ();
+#endif
+        mdate_selected = mdate_interrupt;
+    }
+    else
+    if (!strcmp (name, "tick"))
+    {
+        msg_Dbg (obj, "using Windows time as clock source");
+#if (_WIN32_WINNT < 0x0601)
+        HANDLE h = GetModuleHandle (_T("kernel32.dll"));
+        if (unlikely(h == NULL))
+            abort ();
+        clk.tick.get = (void *)GetProcAddress (h, _T("GetTickCount64"));
+        if (unlikely(clk.tick.get == NULL))
+            abort ();
+#endif
+        mdate_selected = mdate_tick;
+    }
+    else
+    if (!strcmp (name, "multimedia"))
+    {
+        TIMECAPS caps;
+
+        msg_Dbg (obj, "using multimedia timers as clock source");
+        if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
+            abort ();
+        msg_Dbg (obj, " min period: %u ms, max period: %u ms",
+                 caps.wPeriodMin, caps.wPeriodMax);
+        mdate_selected = mdate_multimedia;
+    }
+    else
+    if (!strcmp (name, "perf"))
+    {
+        msg_Dbg (obj, "using performance counters as clock source");
+        if (!QueryPerformanceFrequency (&clk.perf.freq))
+            abort ();
+        msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
+        mdate_selected = mdate_perf;
+    }
+    else
+    if (!strcmp (name, "wall"))
+    {
+        msg_Dbg (obj, "using system time as clock source");
+        mdate_selected = mdate_wall;
+    }
+    else
+    {
+        msg_Err (obj, "invalid clock source \"%s\"", name);
+        abort ();
+    }
+    LeaveCriticalSection (&clock_lock);
+    free (str);
+}
+
+#define xstrdup(str) (strdup(str) ?: (abort(), NULL))
+
+size_t EnumClockSource (vlc_object_t *obj, char ***vp, char ***np)
+{
+    const size_t max = 6;
+    char **values = xmalloc (sizeof (*values) * max);
+    char **names = xmalloc (sizeof (*names) * max);
+    size_t n = 0;
+
+#if (_WIN32_WINNT < 0x0601)
+    DWORD version = LOWORD(GetVersion());
+    version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
+#endif
+
+    values[n] = xstrdup ("");
+    names[n] = xstrdup (_("Auto"));
+    n++;
+#if (_WIN32_WINNT < 0x0601)
+    if (version >= 0x0601)
+#endif
+    {
+        values[n] = xstrdup ("interrupt");
+        names[n] = xstrdup ("Interrupt time");
+        n++;
+    }
+#if (_WIN32_WINNT < 0x0600)
+    if (version >= 0x0600)
+#endif
+    {
+        values[n] = xstrdup ("tick");
+        names[n] = xstrdup ("Windows time");
+        n++;
+    }
+    values[n] = xstrdup ("multimedia");
+    names[n] = xstrdup ("Multimedia timers");
+    n++;
+    values[n] = xstrdup ("perf");
+    names[n] = xstrdup ("Performance counters");
+    n++;
+    values[n] = xstrdup ("wall");
+    names[n] = xstrdup ("System time (DANGEROUS!)");
+    n++;
+
+    *vp = values;
+    *np = names;
+    (void) obj;
+    return n;
+}
+
 
 /*** 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)
 {
     struct vlc_timer *timer = val;
@@ -824,26 +882,6 @@ static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
     assert (timeout);
     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)
 {
@@ -853,46 +891,26 @@ int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
         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 timer)
 {
-#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 timer, bool absolute,
                          mtime_t value, mtime_t interval)
 {
-#ifndef UNDER_CE
     if (timer->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 */
 
@@ -901,28 +919,8 @@ void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
     value = (value + 999) / 1000;
     interval = (interval + 999) / 1000;
 
-#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 ();
 }
 
@@ -936,12 +934,49 @@ unsigned vlc_timer_getoverrun (vlc_timer_t timer)
 /*** CPU ***/
 unsigned vlc_GetCPUCount (void)
 {
-#ifndef UNDER_CE
-    DWORD process;
-    DWORD system;
+    DWORD_PTR process;
+    DWORD_PTR system;
 
     if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
         return popcount (system);
-#endif
      return 1;
 }
+
+
+/*** Initialization ***/
+void vlc_threads_setup (libvlc_int_t *p_libvlc)
+{
+    SelectClockSource (VLC_OBJECT(p_libvlc));
+}
+
+extern vlc_rwlock_t config_lock, msg_lock;
+BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
+
+BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
+{
+    (void) hinstDll;
+    (void) lpvReserved;
+
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            InitializeCriticalSection (&clock_lock);
+            vlc_mutex_init (&super_mutex);
+            vlc_cond_init (&super_variable);
+            vlc_threadvar_create (&thread_key, NULL);
+            vlc_rwlock_init (&config_lock);
+            vlc_rwlock_init (&msg_lock);
+            vlc_CPU_init ();
+            break;
+
+        case DLL_PROCESS_DETACH:
+            vlc_rwlock_destroy (&msg_lock);
+            vlc_rwlock_destroy (&config_lock);
+            vlc_threadvar_delete (&thread_key);
+            vlc_cond_destroy (&super_variable);
+            vlc_mutex_destroy (&super_mutex);
+            DeleteCriticalSection (&clock_lock);
+            break;
+    }
+    return TRUE;
+}