LGPL
[vlc.git] / src / os2 / thread.c
1 /*****************************************************************************
2  * thread.c : OS/2 back-end for LibVLC
3  *****************************************************************************
4  * Copyright (C) 1999-2011 VLC authors and VideoLAN
5  *
6  * Authors: KO Myung-Hun <komh@chollian.net>
7  *          Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *          Clément Sténac
11  *          Rémi Denis-Courmont
12  *          Pierre Ynard
13  *
14  * This program is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU Lesser General Public License as published by
16  * the Free Software Foundation; either version 2.1 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this program; if not, write to the Free Software Foundation,
26  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34
35 #include "libvlc.h"
36 #include <stdarg.h>
37 #include <assert.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <time.h>
41
42 static vlc_threadvar_t thread_key;
43
44 /**
45  * Per-thread data
46  */
47 struct vlc_thread
48 {
49     TID            tid;
50     HEV            cancel_event;
51     HEV            done_event;
52
53     bool           detached;
54     bool           killable;
55     bool           killed;
56     vlc_cleanup_t *cleaners;
57
58     void        *(*entry) (void *);
59     void          *data;
60 };
61
62 static void vlc_cancel_self (PVOID dummy);
63
64 static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout, BOOL fCancelable )
65 {
66     HMUX      hmux;
67     SEMRECORD asr[ 2 ];
68     ULONG     ulUser;
69     int       n;
70     ULONG     rc;
71
72     struct vlc_thread *th = vlc_threadvar_get( thread_key );
73     if( th == NULL || !fCancelable )
74     {
75         /* Main thread - cannot be cancelled anyway
76          * Alien thread - out of our control
77          */
78         if( hev != NULLHANDLE )
79             return DosWaitEventSem( hev, ulTimeout );
80
81         return DosSleep( ulTimeout );
82     }
83
84     n = 0;
85     if( hev != NULLHANDLE )
86     {
87         asr[ n ].hsemCur = ( HSEM )hev;
88         asr[ n ].ulUser  = 0;
89         n++;
90     }
91     asr[ n ].hsemCur = ( HSEM )th->cancel_event;
92     asr[ n ].ulUser  = 0xFFFF;
93     n++;
94
95     DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
96     rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
97     DosCloseMuxWaitSem( hmux );
98     if( rc )
99         return rc;
100
101     if( ulUser == 0xFFFF )
102     {
103         vlc_cancel_self( th );
104         return ERROR_INTERRUPT;
105     }
106
107     return NO_ERROR;
108 }
109
110 static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
111 {
112     return vlc_DosWaitEventSemEx( hev, ulTimeout, TRUE );
113 }
114
115 static ULONG vlc_Sleep (ULONG ulTimeout)
116 {
117     ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout, TRUE );
118
119     return ( rc != ERROR_TIMEOUT ) ? rc : 0;
120 }
121
122 vlc_mutex_t super_mutex;
123 vlc_cond_t  super_variable;
124 extern vlc_rwlock_t config_lock, msg_lock;
125
126 int _CRT_init(void);
127 void _CRT_term(void);
128
129 unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
130
131 unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
132 {
133     VLC_UNUSED (hmod);
134
135     switch (flag)
136     {
137         case 0 :    /* Initialization */
138             if(_CRT_init() == -1)
139                 return 0;
140
141             vlc_mutex_init (&super_mutex);
142             vlc_cond_init (&super_variable);
143             vlc_threadvar_create (&thread_key, NULL);
144             vlc_rwlock_init (&config_lock);
145             vlc_rwlock_init (&msg_lock);
146             vlc_CPU_init ();
147
148             return 1;
149
150         case 1 :    /* Termination */
151             vlc_rwlock_destroy (&msg_lock);
152             vlc_rwlock_destroy (&config_lock);
153             vlc_threadvar_delete (&thread_key);
154             vlc_cond_destroy (&super_variable);
155             vlc_mutex_destroy (&super_mutex);
156
157             _CRT_term();
158
159             return 1;
160     }
161
162     return 0;   /* Failed */
163 }
164
165 /*** Mutexes ***/
166 void vlc_mutex_init( vlc_mutex_t *p_mutex )
167 {
168     /* This creates a recursive mutex. This is OK as fast mutexes have
169      * no defined behavior in case of recursive locking. */
170     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
171     p_mutex->dynamic = true;
172 }
173
174 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
175 {
176     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
177     p_mutex->dynamic = true;
178 }
179
180
181 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
182 {
183     assert (p_mutex->dynamic);
184     DosCloseMutexSem( p_mutex->hmtx );
185 }
186
187 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
188 {
189     if (!p_mutex->dynamic)
190     {   /* static mutexes */
191         int canc = vlc_savecancel ();
192         assert (p_mutex != &super_mutex); /* this one cannot be static */
193
194         vlc_mutex_lock (&super_mutex);
195         while (p_mutex->locked)
196         {
197             p_mutex->contention++;
198             vlc_cond_wait (&super_variable, &super_mutex);
199             p_mutex->contention--;
200         }
201         p_mutex->locked = true;
202         vlc_mutex_unlock (&super_mutex);
203         vlc_restorecancel (canc);
204         return;
205     }
206
207     DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
208 }
209
210 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
211 {
212     if (!p_mutex->dynamic)
213     {   /* static mutexes */
214         int ret = EBUSY;
215
216         assert (p_mutex != &super_mutex); /* this one cannot be static */
217         vlc_mutex_lock (&super_mutex);
218         if (!p_mutex->locked)
219         {
220             p_mutex->locked = true;
221             ret = 0;
222         }
223         vlc_mutex_unlock (&super_mutex);
224         return ret;
225     }
226
227     return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
228 }
229
230 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
231 {
232     if (!p_mutex->dynamic)
233     {   /* static mutexes */
234         assert (p_mutex != &super_mutex); /* this one cannot be static */
235
236         vlc_mutex_lock (&super_mutex);
237         assert (p_mutex->locked);
238         p_mutex->locked = false;
239         if (p_mutex->contention)
240             vlc_cond_broadcast (&super_variable);
241         vlc_mutex_unlock (&super_mutex);
242         return;
243     }
244
245     DosReleaseMutexSem( p_mutex->hmtx );
246 }
247
248 /*** Condition variables ***/
249 enum
250 {
251     CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
252     CLOCK_MONOTONIC,
253 };
254
255 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
256 {
257     /* Create a manual-reset event (manual reset is needed for broadcast). */
258     if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
259         abort();
260     p_condvar->clock = clock;
261 }
262
263 void vlc_cond_init (vlc_cond_t *p_condvar)
264 {
265     vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
266 }
267
268 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
269 {
270     vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
271 }
272
273 void vlc_cond_destroy (vlc_cond_t *p_condvar)
274 {
275     DosCloseEventSem( p_condvar->hev );
276 }
277
278 void vlc_cond_signal (vlc_cond_t *p_condvar)
279 {
280     if (!p_condvar->hev)
281         return;
282
283     /* This is suboptimal but works. */
284     vlc_cond_broadcast (p_condvar);
285 }
286
287 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
288 {
289     if (!p_condvar->hev)
290         return;
291
292     /* Wake all threads up (as the event HANDLE has manual reset) */
293     DosPostEventSem( p_condvar->hev );
294 }
295
296 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
297 {
298     ULONG ulPost;
299     ULONG rc;
300
301     if (!p_condvar->hev)
302     {   /* FIXME FIXME FIXME */
303         msleep (50000);
304         return;
305     }
306
307     do
308     {
309         vlc_testcancel();
310
311         vlc_mutex_unlock (p_mutex);
312         rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT );
313         vlc_mutex_lock (p_mutex);
314     } while( rc == ERROR_INTERRUPT );
315
316     DosResetEventSem( p_condvar->hev, &ulPost );
317 }
318
319 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
320                         mtime_t deadline)
321 {
322     ULONG   ulTimeout;
323     ULONG   ulPost;
324     ULONG   rc;
325
326     if (!p_condvar->hev)
327     {   /* FIXME FIXME FIXME */
328         msleep (50000);
329         return;
330     }
331
332     do
333     {
334         vlc_testcancel();
335
336         mtime_t total;
337         switch (p_condvar->clock)
338         {
339             case CLOCK_REALTIME: /* FIXME? sub-second precision */
340                 total = CLOCK_FREQ * time (NULL);
341                 break;
342             default:
343                 assert (p_condvar->clock == CLOCK_MONOTONIC);
344                 total = mdate();
345                 break;
346         }
347         total = (deadline - total) / 1000;
348         if( total < 0 )
349             total = 0;
350
351         ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
352
353         vlc_mutex_unlock (p_mutex);
354         rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
355         vlc_mutex_lock (p_mutex);
356     } while( rc == ERROR_INTERRUPT );
357
358     DosResetEventSem( p_condvar->hev, &ulPost );
359
360     return rc ? ETIMEDOUT : 0;
361 }
362
363 /*** Semaphore ***/
364 void vlc_sem_init (vlc_sem_t *sem, unsigned value)
365 {
366     if (DosCreateEventSem(NULL, &sem->hev, 0, value > 0 ? TRUE : FALSE))
367         abort ();
368
369     if (DosCreateMutexSem(NULL, &sem->wait_mutex, 0, FALSE))
370         abort ();
371
372     if (DosCreateMutexSem(NULL, &sem->count_mutex, 0, FALSE))
373         abort ();
374
375     sem->count = value;
376 }
377
378 void vlc_sem_destroy (vlc_sem_t *sem)
379 {
380     DosCloseEventSem (sem->hev);
381     DosCloseMutexSem (sem->wait_mutex);
382     DosCloseMutexSem (sem->count_mutex);
383 }
384
385 int vlc_sem_post (vlc_sem_t *sem)
386 {
387     DosRequestMutexSem(sem->count_mutex, SEM_INDEFINITE_WAIT);
388
389     if (sem->count < 0x7FFFFFFF)
390     {
391         sem->count++;
392         DosPostEventSem(sem->hev);
393     }
394
395     DosReleaseMutexSem(sem->count_mutex);
396
397     return 0; /* FIXME */
398 }
399
400 void vlc_sem_wait (vlc_sem_t *sem)
401 {
402     ULONG rc;
403
404     do
405     {
406         vlc_testcancel ();
407
408         DosRequestMutexSem(sem->wait_mutex, SEM_INDEFINITE_WAIT);
409
410         rc = vlc_WaitForSingleObject (sem->hev, SEM_INDEFINITE_WAIT );
411
412         if (!rc)
413         {
414             DosRequestMutexSem(sem->count_mutex, SEM_INDEFINITE_WAIT);
415
416             sem->count--;
417             if (sem->count == 0)
418             {
419                 ULONG ulPost;
420
421                 DosResetEventSem(sem->hev, &ulPost);
422             }
423
424             DosReleaseMutexSem(sem->count_mutex);
425         }
426
427         DosReleaseMutexSem(sem->wait_mutex);
428     } while (rc == ERROR_INTERRUPT);
429 }
430
431 /*** Read/write locks */
432 void vlc_rwlock_init (vlc_rwlock_t *lock)
433 {
434     vlc_mutex_init (&lock->mutex);
435     vlc_cond_init (&lock->wait);
436     lock->readers = 0; /* active readers */
437     lock->writers = 0; /* waiting or active writers */
438     lock->writer = 0; /* ID of active writer */
439 }
440
441 void vlc_rwlock_destroy (vlc_rwlock_t *lock)
442 {
443     vlc_cond_destroy (&lock->wait);
444     vlc_mutex_destroy (&lock->mutex);
445 }
446
447 void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
448 {
449     vlc_mutex_lock (&lock->mutex);
450     /* Recursive read-locking is allowed. With the infos available:
451      *  - the loosest possible condition (no active writer) is:
452      *     (lock->writer != 0)
453      *  - the strictest possible condition is:
454      *     (lock->writer != 0 || (lock->readers == 0 && lock->writers > 0))
455      *  or (lock->readers == 0 && (lock->writer != 0 || lock->writers > 0))
456      */
457     while (lock->writer != 0)
458     {
459         assert (lock->readers == 0);
460         vlc_cond_wait (&lock->wait, &lock->mutex);
461     }
462     if (unlikely(lock->readers == ULONG_MAX))
463         abort ();
464     lock->readers++;
465     vlc_mutex_unlock (&lock->mutex);
466 }
467
468 static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock)
469 {
470     vlc_mutex_lock (&lock->mutex);
471     assert (lock->readers > 0);
472
473     /* If there are no readers left, wake up a writer. */
474     if (--lock->readers == 0 && lock->writers > 0)
475         vlc_cond_signal (&lock->wait);
476     vlc_mutex_unlock (&lock->mutex);
477 }
478
479 void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
480 {
481     vlc_mutex_lock (&lock->mutex);
482     if (unlikely(lock->writers == ULONG_MAX))
483         abort ();
484     lock->writers++;
485     /* Wait until nobody owns the lock in either way. */
486     while ((lock->readers > 0) || (lock->writer != 0))
487         vlc_cond_wait (&lock->wait, &lock->mutex);
488     lock->writers--;
489     assert (lock->writer == 0);
490     lock->writer = _gettid ();
491     vlc_mutex_unlock (&lock->mutex);
492 }
493
494 static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
495 {
496     vlc_mutex_lock (&lock->mutex);
497     assert (lock->writer == _gettid ());
498     assert (lock->readers == 0);
499     lock->writer = 0; /* Write unlock */
500
501     /* Let reader and writer compete. Scheduler decides who wins. */
502     vlc_cond_broadcast (&lock->wait);
503     vlc_mutex_unlock (&lock->mutex);
504 }
505
506 void vlc_rwlock_unlock (vlc_rwlock_t *lock)
507 {
508     /* Note: If the lock is held for reading, lock->writer is nul.
509      * If the lock is held for writing, only this thread can store a value to
510      * lock->writer. Either way, lock->writer is safe to fetch here. */
511     if (lock->writer != 0)
512         vlc_rwlock_wrunlock (lock);
513     else
514         vlc_rwlock_rdunlock (lock);
515 }
516
517 /*** Thread-specific variables (TLS) ***/
518 struct vlc_threadvar
519 {
520     PULONG                id;
521     void                (*destroy) (void *);
522     struct vlc_threadvar *prev;
523     struct vlc_threadvar *next;
524 } *vlc_threadvar_last = NULL;
525
526 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
527 {
528     ULONG rc;
529
530     struct vlc_threadvar *var = malloc (sizeof (*var));
531     if (unlikely(var == NULL))
532         return errno;
533
534     rc = DosAllocThreadLocalMemory( 1, &var->id );
535     if( rc )
536     {
537         free (var);
538         return ENOMEM;
539     }
540
541     var->destroy = destr;
542     var->next = NULL;
543     *p_tls = var;
544
545     vlc_mutex_lock (&super_mutex);
546     var->prev = vlc_threadvar_last;
547     vlc_threadvar_last = var;
548     vlc_mutex_unlock (&super_mutex);
549     return 0;
550 }
551
552 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
553 {
554     struct vlc_threadvar *var = *p_tls;
555
556     vlc_mutex_lock (&super_mutex);
557     if (var->prev != NULL)
558         var->prev->next = var->next;
559     else
560         vlc_threadvar_last = var->next;
561     if (var->next != NULL)
562         var->next->prev = var->prev;
563     vlc_mutex_unlock (&super_mutex);
564
565     DosFreeThreadLocalMemory( var->id );
566     free (var);
567 }
568
569 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
570 {
571     *key->id = ( ULONG )value;
572     return 0;
573 }
574
575 void *vlc_threadvar_get (vlc_threadvar_t key)
576 {
577     return ( void * )*key->id;
578 }
579
580
581 /*** Threads ***/
582 void vlc_threads_setup (libvlc_int_t *p_libvlc)
583 {
584     (void) p_libvlc;
585 }
586
587 static void vlc_thread_cleanup (struct vlc_thread *th)
588 {
589     vlc_threadvar_t key;
590
591 retry:
592     /* TODO: use RW lock or something similar */
593     vlc_mutex_lock (&super_mutex);
594     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
595     {
596         void *value = vlc_threadvar_get (key);
597         if (value != NULL && key->destroy != NULL)
598         {
599             vlc_mutex_unlock (&super_mutex);
600             vlc_threadvar_set (key, NULL);
601             key->destroy (value);
602             goto retry;
603         }
604     }
605     vlc_mutex_unlock (&super_mutex);
606
607     if (th->detached)
608     {
609         DosCloseEventSem (th->cancel_event);
610         DosCloseEventSem (th->done_event );
611         free (th);
612     }
613 }
614
615 static void vlc_entry( void *p )
616 {
617     struct vlc_thread *th = p;
618
619     vlc_threadvar_set (thread_key, th);
620     th->killable = true;
621     th->data = th->entry (th->data);
622     DosPostEventSem( th->done_event );
623     vlc_thread_cleanup (th);
624 }
625
626 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
627                            void *(*entry) (void *), void *data, int priority)
628 {
629     struct vlc_thread *th = malloc (sizeof (*th));
630     if (unlikely(th == NULL))
631         return ENOMEM;
632     th->entry = entry;
633     th->data = data;
634     th->detached = detached;
635     th->killable = false; /* not until vlc_entry() ! */
636     th->killed = false;
637     th->cleaners = NULL;
638
639     if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
640         goto error;
641     if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
642         goto error;
643
644     th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
645     if((int)th->tid == -1)
646         goto error;
647
648     if (p_handle != NULL)
649         *p_handle = th;
650
651     if (priority)
652         DosSetPriority(PRTYS_THREAD,
653                        HIBYTE(priority),
654                        LOBYTE(priority),
655                        th->tid);
656
657     return 0;
658
659 error:
660     DosCloseEventSem (th->cancel_event);
661     DosCloseEventSem (th->done_event);
662     free (th);
663
664     return ENOMEM;
665 }
666
667 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
668                 void *data, int priority)
669 {
670     return vlc_clone_attr (p_handle, false, entry, data, priority);
671 }
672
673 void vlc_join (vlc_thread_t th, void **result)
674 {
675     ULONG rc;
676
677     do
678     {
679         vlc_testcancel();
680         rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
681     } while( rc == ERROR_INTERRUPT );
682
683     if (result != NULL)
684         *result = th->data;
685
686     DosCloseEventSem( th->cancel_event );
687     DosCloseEventSem( th->done_event );
688
689     free( th );
690 }
691
692 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
693                       void *data, int priority)
694 {
695     vlc_thread_t th;
696     if (p_handle == NULL)
697         p_handle = &th;
698
699     return vlc_clone_attr (p_handle, true, entry, data, priority);
700 }
701
702 int vlc_set_priority (vlc_thread_t th, int priority)
703 {
704     if (DosSetPriority(PRTYS_THREAD,
705                        HIBYTE(priority),
706                        LOBYTE(priority),
707                        th->tid))
708         return VLC_EGENERIC;
709     return VLC_SUCCESS;
710 }
711
712 /*** Thread cancellation ***/
713
714 /* APC procedure for thread cancellation */
715 static void vlc_cancel_self (PVOID self)
716 {
717     struct vlc_thread *th = self;
718
719     if (likely(th != NULL))
720         th->killed = true;
721 }
722
723 void vlc_cancel (vlc_thread_t thread_id)
724 {
725     DosPostEventSem( thread_id->cancel_event );
726 }
727
728 int vlc_savecancel (void)
729 {
730     int state;
731
732     struct vlc_thread *th = vlc_threadvar_get (thread_key);
733     if (th == NULL)
734         return false; /* Main thread - cannot be cancelled anyway */
735
736     state = th->killable;
737     th->killable = false;
738     return state;
739 }
740
741 void vlc_restorecancel (int state)
742 {
743     struct vlc_thread *th = vlc_threadvar_get (thread_key);
744     assert (state == false || state == true);
745
746     if (th == NULL)
747         return; /* Main thread - cannot be cancelled anyway */
748
749     assert (!th->killable);
750     th->killable = state != 0;
751 }
752
753 void vlc_testcancel (void)
754 {
755     struct vlc_thread *th = vlc_threadvar_get (thread_key);
756     if (th == NULL)
757         return; /* Main thread - cannot be cancelled anyway */
758
759     /* This check is needed for the case that vlc_cancel() is followed by
760      * vlc_testcancel() without any cancellation point */
761     if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
762         vlc_cancel_self( NULL );
763
764     if (th->killable && th->killed)
765     {
766         for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
767              p->proc (p->data);
768
769         DosPostEventSem( th->done_event );
770         th->data = NULL; /* TODO: special value? */
771         vlc_thread_cleanup (th);
772         _endthread();
773     }
774 }
775
776 void vlc_control_cancel (int cmd, ...)
777 {
778     /* NOTE: This function only modifies thread-specific data, so there is no
779      * need to lock anything. */
780     va_list ap;
781
782     struct vlc_thread *th = vlc_threadvar_get (thread_key);
783     if (th == NULL)
784         return; /* Main thread - cannot be cancelled anyway */
785
786     va_start (ap, cmd);
787     switch (cmd)
788     {
789         case VLC_CLEANUP_PUSH:
790         {
791             /* cleaner is a pointer to the caller stack, no need to allocate
792              * and copy anything. As a nice side effect, this cannot fail. */
793             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
794             cleaner->next = th->cleaners;
795             th->cleaners = cleaner;
796             break;
797         }
798
799         case VLC_CLEANUP_POP:
800         {
801             th->cleaners = th->cleaners->next;
802             break;
803         }
804     }
805     va_end (ap);
806 }
807
808 #define Q2LL( q )   ( *( long long * )&( q ))
809
810 /*** Clock ***/
811 mtime_t mdate (void)
812 {
813     /* We don't need the real date, just the value of a high precision timer */
814     QWORD counter;
815     ULONG freq;
816     if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
817         abort();
818
819     /* Convert to from (1/freq) to microsecond resolution */
820     /* We need to split the division to avoid 63-bits overflow */
821     lldiv_t d = lldiv (Q2LL(counter), freq);
822
823     return (d.quot * 1000000) + ((d.rem * 1000000) / freq);
824 }
825
826 #undef mwait
827 void mwait (mtime_t deadline)
828 {
829     mtime_t delay;
830
831     vlc_testcancel();
832     while ((delay = (deadline - mdate())) > 0)
833     {
834         delay /= 1000;
835         if (unlikely(delay > 0x7fffffff))
836             delay = 0x7fffffff;
837         vlc_Sleep (delay);
838         vlc_testcancel();
839     }
840 }
841
842 #undef msleep
843 void msleep (mtime_t delay)
844 {
845     mwait (mdate () + delay);
846 }
847
848 /*** Timers ***/
849 struct vlc_timer
850 {
851     TID    tid;
852     HEV    hev;
853     HTIMER htimer;
854     ULONG  interval;
855     bool   quit;
856     void (*func) (void *);
857     void  *data;
858 };
859
860 static void vlc_timer_do (void *arg)
861 {
862     struct vlc_timer *timer = arg;
863
864     while (1)
865     {
866         ULONG count;
867
868         DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
869         DosResetEventSem (timer->hev, &count);
870
871         if (timer->quit)
872             break;
873
874         timer->func (timer->data);
875
876         if (timer->interval)
877             DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
878     }
879 }
880
881 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
882 {
883     struct vlc_timer *timer = malloc (sizeof (*timer));
884
885     if (timer == NULL)
886         return ENOMEM;
887
888     timer->func = func;
889     timer->data = data;
890
891     DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
892     timer->htimer = NULLHANDLE;
893     timer->interval = 0;
894     timer->quit = false;
895     timer->tid  = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
896
897     *id = timer;
898     return 0;
899 }
900
901 void vlc_timer_destroy (vlc_timer_t timer)
902 {
903     if (timer->htimer != NULLHANDLE)
904         DosStopTimer (timer->htimer);
905
906     timer->quit = true;
907     DosPostEventSem (timer->hev);
908     DosWaitThread (&timer->tid, DCWW_WAIT);
909     DosCloseEventSem (timer->hev);
910
911     free (timer);
912 }
913
914 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
915                          mtime_t value, mtime_t interval)
916 {
917     if (timer->htimer != NULLHANDLE)
918     {
919         DosStopTimer (timer->htimer);
920         timer->htimer = NULLHANDLE;
921         timer->interval = 0;
922     }
923
924     if (value == 0)
925         return; /* Disarm */
926
927     if (absolute)
928         value -= mdate ();
929     value = (value + 999) / 1000;
930     interval = (interval + 999) / 1000;
931
932     timer->interval = interval;
933     if (DosAsyncTimer (value, (HSEM)timer->hev, &timer->htimer))
934         abort ();
935 }
936
937 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
938 {
939     (void)timer;
940     return 0;
941 }
942
943 /*** CPU ***/
944 unsigned vlc_GetCPUCount (void)
945 {
946     ULONG numprocs = 1;
947
948     DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
949                     &numprocs, sizeof(numprocs));
950
951     return numprocs;
952 }