]> git.sesse.net Git - vlc/blob - src/win32/thread.c
rwlock: reduce footprint and factor Win32 and OS/2 back-ends
[vlc] / src / win32 / thread.c
1 /*****************************************************************************
2  * thread.c : Win32 back-end for LibVLC
3  *****************************************************************************
4  * Copyright (C) 1999-2009 VLC authors and VideoLAN
5  *
6  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
7  *          Samuel Hocevar <sam@zoy.org>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *          Clément Sténac
10  *          Rémi Denis-Courmont
11  *          Pierre Ynard
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include "libvlc.h"
35 #include <stdarg.h>
36 #include <assert.h>
37 #include <limits.h>
38 #include <errno.h>
39
40 /*** Static mutex and condition variable ***/
41 static vlc_mutex_t super_mutex;
42 static vlc_cond_t  super_variable;
43
44
45 /*** Common helpers ***/
46 static DWORD vlc_WaitForMultipleObjects (DWORD count, const HANDLE *handles,
47                                          DWORD delay)
48 {
49     DWORD ret;
50     if (count == 0)
51     {
52         ret = SleepEx (delay, TRUE);
53         if (ret == 0)
54             ret = WAIT_TIMEOUT;
55     }
56     else
57         ret = WaitForMultipleObjectsEx (count, handles, FALSE, delay, TRUE);
58
59     /* We do not abandon objects... this would be a bug */
60     assert (ret < WAIT_ABANDONED_0 || WAIT_ABANDONED_0 + count - 1 < ret);
61
62     if (unlikely(ret == WAIT_FAILED))
63         abort (); /* We are screwed! */
64     return ret;
65 }
66
67 static DWORD vlc_WaitForSingleObject (HANDLE handle, DWORD delay)
68 {
69     return vlc_WaitForMultipleObjects (1, &handle, delay);
70 }
71
72 static DWORD vlc_Sleep (DWORD delay)
73 {
74     DWORD ret = vlc_WaitForMultipleObjects (0, NULL, delay);
75     return (ret != WAIT_TIMEOUT) ? ret : 0;
76 }
77
78
79 /*** Mutexes ***/
80 void vlc_mutex_init( vlc_mutex_t *p_mutex )
81 {
82     /* This creates a recursive mutex. This is OK as fast mutexes have
83      * no defined behavior in case of recursive locking. */
84     InitializeCriticalSection (&p_mutex->mutex);
85     p_mutex->dynamic = true;
86 }
87
88 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
89 {
90     InitializeCriticalSection( &p_mutex->mutex );
91     p_mutex->dynamic = true;
92 }
93
94
95 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
96 {
97     assert (p_mutex->dynamic);
98     DeleteCriticalSection (&p_mutex->mutex);
99 }
100
101 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
102 {
103     if (!p_mutex->dynamic)
104     {   /* static mutexes */
105         int canc = vlc_savecancel ();
106         assert (p_mutex != &super_mutex); /* this one cannot be static */
107
108         vlc_mutex_lock (&super_mutex);
109         while (p_mutex->locked)
110         {
111             p_mutex->contention++;
112             vlc_cond_wait (&super_variable, &super_mutex);
113             p_mutex->contention--;
114         }
115         p_mutex->locked = true;
116         vlc_mutex_unlock (&super_mutex);
117         vlc_restorecancel (canc);
118         return;
119     }
120
121     EnterCriticalSection (&p_mutex->mutex);
122 }
123
124 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
125 {
126     if (!p_mutex->dynamic)
127     {   /* static mutexes */
128         int ret = EBUSY;
129
130         assert (p_mutex != &super_mutex); /* this one cannot be static */
131         vlc_mutex_lock (&super_mutex);
132         if (!p_mutex->locked)
133         {
134             p_mutex->locked = true;
135             ret = 0;
136         }
137         vlc_mutex_unlock (&super_mutex);
138         return ret;
139     }
140
141     return TryEnterCriticalSection (&p_mutex->mutex) ? 0 : EBUSY;
142 }
143
144 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
145 {
146     if (!p_mutex->dynamic)
147     {   /* static mutexes */
148         assert (p_mutex != &super_mutex); /* this one cannot be static */
149
150         vlc_mutex_lock (&super_mutex);
151         assert (p_mutex->locked);
152         p_mutex->locked = false;
153         if (p_mutex->contention)
154             vlc_cond_broadcast (&super_variable);
155         vlc_mutex_unlock (&super_mutex);
156         return;
157     }
158
159     LeaveCriticalSection (&p_mutex->mutex);
160 }
161
162 /*** Condition variables ***/
163 enum
164 {
165     CLOCK_STATIC=0, /* must be zero for VLC_STATIC_COND */
166     CLOCK_MONOTONIC,
167     CLOCK_REALTIME,
168 };
169
170 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
171 {
172     /* Create a manual-reset event (manual reset is needed for broadcast). */
173     p_condvar->handle = CreateEvent (NULL, TRUE, FALSE, NULL);
174     if (!p_condvar->handle)
175         abort();
176     p_condvar->clock = clock;
177 }
178
179 void vlc_cond_init (vlc_cond_t *p_condvar)
180 {
181     vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
182 }
183
184 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
185 {
186     vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
187 }
188
189 void vlc_cond_destroy (vlc_cond_t *p_condvar)
190 {
191     CloseHandle (p_condvar->handle);
192 }
193
194 void vlc_cond_signal (vlc_cond_t *p_condvar)
195 {
196     if (!p_condvar->clock)
197         return;
198
199     /* This is suboptimal but works. */
200     vlc_cond_broadcast (p_condvar);
201 }
202
203 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
204 {
205     if (!p_condvar->clock)
206         return;
207
208     /* Wake all threads up (as the event HANDLE has manual reset) */
209     SetEvent (p_condvar->handle);
210 }
211
212 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
213 {
214     DWORD result;
215
216     if (!p_condvar->clock)
217     {   /* FIXME FIXME FIXME */
218         msleep (50000);
219         return;
220     }
221
222     do
223     {
224         vlc_testcancel ();
225         vlc_mutex_unlock (p_mutex);
226         result = vlc_WaitForSingleObject (p_condvar->handle, INFINITE);
227         vlc_mutex_lock (p_mutex);
228     }
229     while (result == WAIT_IO_COMPLETION);
230
231     ResetEvent (p_condvar->handle);
232 }
233
234 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
235                         mtime_t deadline)
236 {
237     DWORD result;
238
239     do
240     {
241         vlc_testcancel ();
242
243         mtime_t total;
244         switch (p_condvar->clock)
245         {
246             case CLOCK_MONOTONIC:
247                 total = mdate();
248                 break;
249             case CLOCK_REALTIME: /* FIXME? sub-second precision */
250                 total = CLOCK_FREQ * time (NULL);
251                 break;
252             default:
253                 assert (!p_condvar->clock);
254                 /* FIXME FIXME FIXME */
255                 msleep (50000);
256                 return 0;
257         }
258         total = (deadline - total) / 1000;
259         if( total < 0 )
260             total = 0;
261
262         DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total;
263         vlc_mutex_unlock (p_mutex);
264         result = vlc_WaitForSingleObject (p_condvar->handle, delay);
265         vlc_mutex_lock (p_mutex);
266     }
267     while (result == WAIT_IO_COMPLETION);
268
269     ResetEvent (p_condvar->handle);
270
271     return (result == WAIT_OBJECT_0) ? 0 : ETIMEDOUT;
272 }
273
274 /*** Semaphore ***/
275 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
276 {
277     *sem = CreateSemaphore (NULL, value, 0x7fffffff, NULL);
278     if (*sem == NULL)
279         abort ();
280 }
281
282 void vlc_sem_destroy (vlc_sem_t *sem)
283 {
284     CloseHandle (*sem);
285 }
286
287 int vlc_sem_post (vlc_sem_t *sem)
288 {
289     ReleaseSemaphore (*sem, 1, NULL);
290     return 0; /* FIXME */
291 }
292
293 void vlc_sem_wait (vlc_sem_t *sem)
294 {
295     DWORD result;
296
297     do
298     {
299         vlc_testcancel ();
300         result = vlc_WaitForSingleObject (*sem, INFINITE);
301     }
302     while (result == WAIT_IO_COMPLETION);
303 }
304
305 /*** Read/write locks */
306 #if 0
307 /* SRW (Slim Read Write) locks are available in Vista+ only */
308 void vlc_rwlock_init (vlc_rwlock_t *lock)
309 {
310 }
311
312 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
313 {
314 }
315
316 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
317 {
318 }
319
320 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
321 {
322 }
323
324 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
325 {
326 }
327 #else
328 # include "misc/rwlock.h"
329 #endif
330
331 /*** Thread-specific variables (TLS) ***/
332 struct vlc_threadvar
333 {
334     DWORD                 id;
335     void                (*destroy) (void *);
336     struct vlc_threadvar *prev;
337     struct vlc_threadvar *next;
338 } *vlc_threadvar_last = NULL;
339
340 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
341 {
342     struct vlc_threadvar *var = malloc (sizeof (*var));
343     if (unlikely(var == NULL))
344         return errno;
345
346     var->id = TlsAlloc();
347     if (var->id == TLS_OUT_OF_INDEXES)
348     {
349         free (var);
350         return EAGAIN;
351     }
352     var->destroy = destr;
353     var->next = NULL;
354     *p_tls = var;
355
356     vlc_mutex_lock (&super_mutex);
357     var->prev = vlc_threadvar_last;
358     if (var->prev)
359         var->prev->next = var;
360
361     vlc_threadvar_last = var;
362     vlc_mutex_unlock (&super_mutex);
363     return 0;
364 }
365
366 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
367 {
368     struct vlc_threadvar *var = *p_tls;
369
370     vlc_mutex_lock (&super_mutex);
371     if (var->prev != NULL)
372         var->prev->next = var->next;
373
374     if (var->next != NULL)
375         var->next->prev = var->prev;
376     else
377         vlc_threadvar_last = var->prev;
378
379     vlc_mutex_unlock (&super_mutex);
380
381     TlsFree (var->id);
382     free (var);
383 }
384
385 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
386 {
387     return TlsSetValue (key->id, value) ? ENOMEM : 0;
388 }
389
390 void *vlc_threadvar_get (vlc_threadvar_t key)
391 {
392     return TlsGetValue (key->id);
393 }
394
395 /*** Threads ***/
396 static vlc_threadvar_t thread_key;
397
398 /** Per-thread data */
399 struct vlc_thread
400 {
401     HANDLE         id;
402
403     bool           detached;
404     bool           killable;
405     bool           killed;
406     vlc_cleanup_t *cleaners;
407
408     void        *(*entry) (void *);
409     void          *data;
410 };
411
412 static void vlc_thread_cleanup (struct vlc_thread *th)
413 {
414     vlc_threadvar_t key;
415
416 retry:
417     /* TODO: use RW lock or something similar */
418     vlc_mutex_lock (&super_mutex);
419     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
420     {
421         void *value = vlc_threadvar_get (key);
422         if (value != NULL && key->destroy != NULL)
423         {
424             vlc_mutex_unlock (&super_mutex);
425             vlc_threadvar_set (key, NULL);
426             key->destroy (value);
427             goto retry;
428         }
429     }
430     vlc_mutex_unlock (&super_mutex);
431
432     if (th->detached)
433     {
434         CloseHandle (th->id);
435         free (th);
436     }
437 }
438
439 static unsigned __stdcall vlc_entry (void *p)
440 {
441     struct vlc_thread *th = p;
442
443     vlc_threadvar_set (thread_key, th);
444     th->killable = true;
445     th->data = th->entry (th->data);
446     vlc_thread_cleanup (th);
447     return 0;
448 }
449
450 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
451                            void *(*entry) (void *), void *data, int priority)
452 {
453     struct vlc_thread *th = malloc (sizeof (*th));
454     if (unlikely(th == NULL))
455         return ENOMEM;
456     th->entry = entry;
457     th->data = data;
458     th->detached = detached;
459     th->killable = false; /* not until vlc_entry() ! */
460     th->killed = false;
461     th->cleaners = NULL;
462
463     HANDLE hThread;
464     /* When using the MSVCRT C library you have to use the _beginthreadex
465      * function instead of CreateThread, otherwise you'll end up with
466      * memory leaks and the signal functions not working (see Microsoft
467      * Knowledge Base, article 104641) */
468     uintptr_t h;
469
470     h = _beginthreadex (NULL, 0, vlc_entry, th, CREATE_SUSPENDED, NULL);
471     if (h == 0)
472     {
473         int err = errno;
474         free (th);
475         return err;
476     }
477     hThread = (HANDLE)h;
478
479     /* Thread is suspended, so we can safely set th->id */
480     th->id = hThread;
481     if (p_handle != NULL)
482         *p_handle = th;
483
484     if (priority)
485         SetThreadPriority (hThread, priority);
486
487     ResumeThread (hThread);
488
489     return 0;
490 }
491
492 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
493                 void *data, int priority)
494 {
495     return vlc_clone_attr (p_handle, false, entry, data, priority);
496 }
497
498 void vlc_join (vlc_thread_t th, void **result)
499 {
500     do
501         vlc_testcancel ();
502     while (vlc_WaitForSingleObject (th->id, INFINITE) == WAIT_IO_COMPLETION);
503
504     if (result != NULL)
505         *result = th->data;
506     CloseHandle (th->id);
507     free (th);
508 }
509
510 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
511                       void *data, int priority)
512 {
513     vlc_thread_t th;
514     if (p_handle == NULL)
515         p_handle = &th;
516
517     return vlc_clone_attr (p_handle, true, entry, data, priority);
518 }
519
520 int vlc_set_priority (vlc_thread_t th, int priority)
521 {
522     if (!SetThreadPriority (th->id, priority))
523         return VLC_EGENERIC;
524     return VLC_SUCCESS;
525 }
526
527 /*** Thread cancellation ***/
528
529 /* APC procedure for thread cancellation */
530 static void CALLBACK vlc_cancel_self (ULONG_PTR self)
531 {
532     struct vlc_thread *th = (void *)self;
533
534     if (likely(th != NULL))
535         th->killed = true;
536 }
537
538 void vlc_cancel (vlc_thread_t th)
539 {
540     QueueUserAPC (vlc_cancel_self, th->id, (uintptr_t)th);
541 }
542
543 int vlc_savecancel (void)
544 {
545     struct vlc_thread *th = vlc_threadvar_get (thread_key);
546     if (th == NULL)
547         return false; /* Main thread - cannot be cancelled anyway */
548
549     int state = th->killable;
550     th->killable = false;
551     return state;
552 }
553
554 void vlc_restorecancel (int state)
555 {
556     struct vlc_thread *th = vlc_threadvar_get (thread_key);
557     assert (state == false || state == true);
558
559     if (th == NULL)
560         return; /* Main thread - cannot be cancelled anyway */
561
562     assert (!th->killable);
563     th->killable = state != 0;
564 }
565
566 void vlc_testcancel (void)
567 {
568     struct vlc_thread *th = vlc_threadvar_get (thread_key);
569     if (th == NULL)
570         return; /* Main thread - cannot be cancelled anyway */
571
572     if (th->killable && th->killed)
573     {
574         for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
575              p->proc (p->data);
576
577         th->data = NULL; /* TODO: special value? */
578         vlc_thread_cleanup (th);
579         _endthreadex(0);
580     }
581 }
582
583 void vlc_control_cancel (int cmd, ...)
584 {
585     /* NOTE: This function only modifies thread-specific data, so there is no
586      * need to lock anything. */
587     va_list ap;
588
589     struct vlc_thread *th = vlc_threadvar_get (thread_key);
590     if (th == NULL)
591         return; /* Main thread - cannot be cancelled anyway */
592
593     va_start (ap, cmd);
594     switch (cmd)
595     {
596         case VLC_CLEANUP_PUSH:
597         {
598             /* cleaner is a pointer to the caller stack, no need to allocate
599              * and copy anything. As a nice side effect, this cannot fail. */
600             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
601             cleaner->next = th->cleaners;
602             th->cleaners = cleaner;
603             break;
604         }
605
606         case VLC_CLEANUP_POP:
607         {
608             th->cleaners = th->cleaners->next;
609             break;
610         }
611     }
612     va_end (ap);
613 }
614
615 /*** Clock ***/
616 static CRITICAL_SECTION clock_lock;
617
618 static mtime_t mdate_giveup (void)
619 {
620     abort ();
621 }
622
623 static mtime_t (*mdate_selected) (void) = mdate_giveup;
624
625 mtime_t mdate (void)
626 {
627     return mdate_selected ();
628 }
629
630 static union
631 {
632     struct
633     {
634 #if (_WIN32_WINNT < 0x0601)
635         BOOL (*query) (PULONGLONG);
636 #endif
637     } interrupt;
638     struct
639     {
640 #if (_WIN32_WINNT < 0x0600)
641         ULONGLONG (*get) (void);
642 #endif
643     } tick;
644     struct
645     {
646         LARGE_INTEGER freq;
647     } perf;
648 } clk;
649
650 static mtime_t mdate_interrupt (void)
651 {
652     ULONGLONG ts;
653     BOOL ret;
654
655 #if (_WIN32_WINNT >= 0x0601)
656     ret = QueryUnbiasedInterruptTime (&ts);
657 #else
658     ret = clk.interrupt.query (&ts);
659 #endif
660     if (unlikely(!ret))
661         abort ();
662
663     /* hundreds of nanoseconds */
664     static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
665     return ts / (10000000 / CLOCK_FREQ);
666 }
667
668 static mtime_t mdate_tick (void)
669 {
670 #if (_WIN32_WINNT >= 0x0600)
671     ULONGLONG ts = GetTickCount64 ();
672 #else
673     ULONGLONG ts = clk.tick.get ();
674 #endif
675
676     /* milliseconds */
677     static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
678     return ts * (CLOCK_FREQ / 1000);
679 }
680 #include <mmsystem.h>
681 static mtime_t mdate_multimedia (void)
682 {
683      DWORD ts = timeGetTime ();
684
685     /* milliseconds */
686     static_assert ((CLOCK_FREQ % 1000) == 0, "Broken frequencies ratio");
687     return ts * (CLOCK_FREQ / 1000);
688 }
689
690 static mtime_t mdate_perf (void)
691 {
692     /* We don't need the real date, just the value of a high precision timer */
693     LARGE_INTEGER counter;
694     if (!QueryPerformanceCounter (&counter))
695         abort ();
696
697     /* Convert to from (1/freq) to microsecond resolution */
698     /* We need to split the division to avoid 63-bits overflow */
699     lldiv_t d = lldiv (counter.QuadPart, clk.perf.freq.QuadPart);
700
701     return (d.quot * 1000000) + ((d.rem * 1000000) / clk.perf.freq.QuadPart);
702 }
703
704 static mtime_t mdate_wall (void)
705 {
706     FILETIME ts;
707     ULARGE_INTEGER s;
708
709 #if (_WIN32_WINNT >= 0x0602)
710     GetSystemTimePreciseAsFileTime (&ts);
711 #else
712     GetSystemTimeAsFileTime (&ts);
713 #endif
714     s.LowPart = ts.dwLowDateTime;
715     s.HighPart = ts.dwHighDateTime;
716     /* hundreds of nanoseconds */
717     static_assert ((10000000 % CLOCK_FREQ) == 0, "Broken frequencies ratio");
718     return s.QuadPart / (10000000 / CLOCK_FREQ);
719 }
720
721 #undef mwait
722 void mwait (mtime_t deadline)
723 {
724     mtime_t delay;
725
726     vlc_testcancel();
727     while ((delay = (deadline - mdate())) > 0)
728     {
729         delay /= 1000;
730         if (unlikely(delay > 0x7fffffff))
731             delay = 0x7fffffff;
732         vlc_Sleep (delay);
733         vlc_testcancel();
734     }
735 }
736
737 #undef msleep
738 void msleep (mtime_t delay)
739 {
740     mwait (mdate () + delay);
741 }
742
743 static void SelectClockSource (vlc_object_t *obj)
744 {
745     EnterCriticalSection (&clock_lock);
746     if (mdate_selected != mdate_giveup)
747     {
748         LeaveCriticalSection (&clock_lock);
749         return;
750     }
751
752     const char *name = "perf";
753     char *str = var_InheritString (obj, "clock-source");
754     if (str != NULL)
755         name = str;
756     if (!strcmp (name, "interrupt"))
757     {
758         msg_Dbg (obj, "using interrupt time as clock source");
759 #if (_WIN32_WINNT < 0x0601)
760         HANDLE h = GetModuleHandle (_T("kernel32.dll"));
761         if (unlikely(h == NULL))
762             abort ();
763         clk.interrupt.query = (void *)GetProcAddress (h,
764                                                       _T("QueryUnbiasedInterruptTime"));
765         if (unlikely(clk.interrupt.query == NULL))
766             abort ();
767 #endif
768         mdate_selected = mdate_interrupt;
769     }
770     else
771     if (!strcmp (name, "tick"))
772     {
773         msg_Dbg (obj, "using Windows time as clock source");
774 #if (_WIN32_WINNT < 0x0601)
775         HANDLE h = GetModuleHandle (_T("kernel32.dll"));
776         if (unlikely(h == NULL))
777             abort ();
778         clk.tick.get = (void *)GetProcAddress (h, _T("GetTickCount64"));
779         if (unlikely(clk.tick.get == NULL))
780             abort ();
781 #endif
782         mdate_selected = mdate_tick;
783     }
784     else
785     if (!strcmp (name, "multimedia"))
786     {
787         TIMECAPS caps;
788
789         msg_Dbg (obj, "using multimedia timers as clock source");
790         if (timeGetDevCaps (&caps, sizeof (caps)) != MMSYSERR_NOERROR)
791             abort ();
792         msg_Dbg (obj, " min period: %u ms, max period: %u ms",
793                  caps.wPeriodMin, caps.wPeriodMax);
794         mdate_selected = mdate_multimedia;
795     }
796     else
797     if (!strcmp (name, "perf"))
798     {
799         msg_Dbg (obj, "using performance counters as clock source");
800         if (!QueryPerformanceFrequency (&clk.perf.freq))
801             abort ();
802         msg_Dbg (obj, " frequency: %llu Hz", clk.perf.freq.QuadPart);
803         mdate_selected = mdate_perf;
804     }
805     else
806     if (!strcmp (name, "wall"))
807     {
808         msg_Dbg (obj, "using system time as clock source");
809         mdate_selected = mdate_wall;
810     }
811     else
812     {
813         msg_Err (obj, "invalid clock source \"%s\"", name);
814         abort ();
815     }
816     LeaveCriticalSection (&clock_lock);
817     free (str);
818 }
819
820 #define xstrdup(str) (strdup(str) ?: (abort(), NULL))
821
822 size_t EnumClockSource (vlc_object_t *obj, char ***vp, char ***np)
823 {
824     const size_t max = 6;
825     char **values = xmalloc (sizeof (*values) * max);
826     char **names = xmalloc (sizeof (*names) * max);
827     size_t n = 0;
828
829 #if (_WIN32_WINNT < 0x0601)
830     DWORD version = LOWORD(GetVersion());
831     version = (LOBYTE(version) << 8) | (HIBYTE(version) << 0);
832 #endif
833
834     values[n] = xstrdup ("");
835     names[n] = xstrdup (_("Auto"));
836     n++;
837 #if (_WIN32_WINNT < 0x0601)
838     if (version >= 0x0601)
839 #endif
840     {
841         values[n] = xstrdup ("interrupt");
842         names[n] = xstrdup ("Interrupt time");
843         n++;
844     }
845 #if (_WIN32_WINNT < 0x0600)
846     if (version >= 0x0600)
847 #endif
848     {
849         values[n] = xstrdup ("tick");
850         names[n] = xstrdup ("Windows time");
851         n++;
852     }
853     values[n] = xstrdup ("multimedia");
854     names[n] = xstrdup ("Multimedia timers");
855     n++;
856     values[n] = xstrdup ("perf");
857     names[n] = xstrdup ("Performance counters");
858     n++;
859     values[n] = xstrdup ("wall");
860     names[n] = xstrdup ("System time (DANGEROUS!)");
861     n++;
862
863     *vp = values;
864     *np = names;
865     (void) obj;
866     return n;
867 }
868
869
870 /*** Timers ***/
871 struct vlc_timer
872 {
873     HANDLE handle;
874     void (*func) (void *);
875     void *data;
876 };
877
878 static void CALLBACK vlc_timer_do (void *val, BOOLEAN timeout)
879 {
880     struct vlc_timer *timer = val;
881
882     assert (timeout);
883     timer->func (timer->data);
884 }
885
886 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
887 {
888     struct vlc_timer *timer = malloc (sizeof (*timer));
889
890     if (timer == NULL)
891         return ENOMEM;
892     timer->func = func;
893     timer->data = data;
894     timer->handle = INVALID_HANDLE_VALUE;
895     *id = timer;
896     return 0;
897 }
898
899 void vlc_timer_destroy (vlc_timer_t timer)
900 {
901     if (timer->handle != INVALID_HANDLE_VALUE)
902         DeleteTimerQueueTimer (NULL, timer->handle, INVALID_HANDLE_VALUE);
903     free (timer);
904 }
905
906 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
907                          mtime_t value, mtime_t interval)
908 {
909     if (timer->handle != INVALID_HANDLE_VALUE)
910     {
911         DeleteTimerQueueTimer (NULL, timer->handle, NULL);
912         timer->handle = INVALID_HANDLE_VALUE;
913     }
914     if (value == 0)
915         return; /* Disarm */
916
917     if (absolute)
918         value -= mdate ();
919     value = (value + 999) / 1000;
920     interval = (interval + 999) / 1000;
921
922     if (!CreateTimerQueueTimer (&timer->handle, NULL, vlc_timer_do, timer,
923                                 value, interval, WT_EXECUTEDEFAULT))
924         abort ();
925 }
926
927 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
928 {
929     (void)timer;
930     return 0;
931 }
932
933
934 /*** CPU ***/
935 unsigned vlc_GetCPUCount (void)
936 {
937     DWORD_PTR process;
938     DWORD_PTR system;
939
940     if (GetProcessAffinityMask (GetCurrentProcess(), &process, &system))
941         return popcount (system);
942      return 1;
943 }
944
945
946 /*** Initialization ***/
947 void vlc_threads_setup (libvlc_int_t *p_libvlc)
948 {
949     SelectClockSource (VLC_OBJECT(p_libvlc));
950 }
951
952 extern vlc_rwlock_t config_lock, msg_lock;
953 BOOL WINAPI DllMain (HINSTANCE, DWORD, LPVOID);
954
955 BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
956 {
957     (void) hinstDll;
958     (void) lpvReserved;
959
960     switch (fdwReason)
961     {
962         case DLL_PROCESS_ATTACH:
963             InitializeCriticalSection (&clock_lock);
964             vlc_mutex_init (&super_mutex);
965             vlc_cond_init (&super_variable);
966             vlc_threadvar_create (&thread_key, NULL);
967             vlc_rwlock_init (&config_lock);
968             vlc_rwlock_init (&msg_lock);
969             vlc_CPU_init ();
970             break;
971
972         case DLL_PROCESS_DETACH:
973             vlc_rwlock_destroy (&msg_lock);
974             vlc_rwlock_destroy (&config_lock);
975             vlc_threadvar_delete (&thread_key);
976             vlc_cond_destroy (&super_variable);
977             vlc_mutex_destroy (&super_mutex);
978             DeleteCriticalSection (&clock_lock);
979             break;
980     }
981     return TRUE;
982 }