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