]> git.sesse.net Git - vlc/blob - src/os2/thread.c
decoder: drain the audio output properly
[vlc] / 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 #include <sys/types.h>
43 #include <sys/socket.h>
44
45 #include <sys/time.h>
46 #include <sys/select.h>
47
48 static vlc_threadvar_t thread_key;
49
50 /**
51  * Per-thread data
52  */
53 struct vlc_thread
54 {
55     TID            tid;
56     HEV            cancel_event;
57     HEV            done_event;
58     int            cancel_sock;
59
60     bool           detached;
61     bool           killable;
62     bool           killed;
63     vlc_cleanup_t *cleaners;
64
65     void        *(*entry) (void *);
66     void          *data;
67 };
68
69 static void vlc_cancel_self (PVOID dummy);
70
71 static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout, BOOL fCancelable )
72 {
73     HMUX      hmux;
74     SEMRECORD asr[ 2 ];
75     ULONG     ulUser;
76     int       n;
77     ULONG     rc;
78
79     struct vlc_thread *th = vlc_threadvar_get( thread_key );
80     if( th == NULL || !fCancelable )
81     {
82         /* Main thread - cannot be cancelled anyway
83          * Alien thread - out of our control
84          */
85         if( hev != NULLHANDLE )
86             return DosWaitEventSem( hev, ulTimeout );
87
88         return DosSleep( ulTimeout );
89     }
90
91     n = 0;
92     if( hev != NULLHANDLE )
93     {
94         asr[ n ].hsemCur = ( HSEM )hev;
95         asr[ n ].ulUser  = 0;
96         n++;
97     }
98     asr[ n ].hsemCur = ( HSEM )th->cancel_event;
99     asr[ n ].ulUser  = 0xFFFF;
100     n++;
101
102     DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
103     rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
104     DosCloseMuxWaitSem( hmux );
105     if( rc )
106         return rc;
107
108     if( ulUser == 0xFFFF )
109     {
110         vlc_cancel_self( th );
111         return ERROR_INTERRUPT;
112     }
113
114     return NO_ERROR;
115 }
116
117 static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
118 {
119     return vlc_DosWaitEventSemEx( hev, ulTimeout, TRUE );
120 }
121
122 static ULONG vlc_Sleep (ULONG ulTimeout)
123 {
124     ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout, TRUE );
125
126     return ( rc != ERROR_TIMEOUT ) ? rc : 0;
127 }
128
129 static vlc_mutex_t super_mutex;
130 static vlc_cond_t  super_variable;
131 extern vlc_rwlock_t config_lock;
132
133 int _CRT_init(void);
134 void _CRT_term(void);
135
136 unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
137
138 unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
139 {
140     VLC_UNUSED (hmod);
141
142     switch (flag)
143     {
144         case 0 :    /* Initialization */
145             if(_CRT_init() == -1)
146                 return 0;
147
148             vlc_mutex_init (&super_mutex);
149             vlc_cond_init (&super_variable);
150             vlc_threadvar_create (&thread_key, NULL);
151             vlc_rwlock_init (&config_lock);
152             vlc_CPU_init ();
153
154             return 1;
155
156         case 1 :    /* Termination */
157             vlc_rwlock_destroy (&config_lock);
158             vlc_threadvar_delete (&thread_key);
159             vlc_cond_destroy (&super_variable);
160             vlc_mutex_destroy (&super_mutex);
161
162             _CRT_term();
163
164             return 1;
165     }
166
167     return 0;   /* Failed */
168 }
169
170 /*** Mutexes ***/
171 void vlc_mutex_init( vlc_mutex_t *p_mutex )
172 {
173     /* This creates a recursive mutex. This is OK as fast mutexes have
174      * no defined behavior in case of recursive locking. */
175     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
176     p_mutex->dynamic = true;
177 }
178
179 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
180 {
181     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
182     p_mutex->dynamic = true;
183 }
184
185
186 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
187 {
188     assert (p_mutex->dynamic);
189     DosCloseMutexSem( p_mutex->hmtx );
190 }
191
192 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
193 {
194     if (!p_mutex->dynamic)
195     {   /* static mutexes */
196         int canc = vlc_savecancel ();
197         assert (p_mutex != &super_mutex); /* this one cannot be static */
198
199         vlc_mutex_lock (&super_mutex);
200         while (p_mutex->locked)
201         {
202             p_mutex->contention++;
203             vlc_cond_wait (&super_variable, &super_mutex);
204             p_mutex->contention--;
205         }
206         p_mutex->locked = true;
207         vlc_mutex_unlock (&super_mutex);
208         vlc_restorecancel (canc);
209         return;
210     }
211
212     DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
213 }
214
215 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
216 {
217     if (!p_mutex->dynamic)
218     {   /* static mutexes */
219         int ret = EBUSY;
220
221         assert (p_mutex != &super_mutex); /* this one cannot be static */
222         vlc_mutex_lock (&super_mutex);
223         if (!p_mutex->locked)
224         {
225             p_mutex->locked = true;
226             ret = 0;
227         }
228         vlc_mutex_unlock (&super_mutex);
229         return ret;
230     }
231
232     return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
233 }
234
235 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
236 {
237     if (!p_mutex->dynamic)
238     {   /* static mutexes */
239         assert (p_mutex != &super_mutex); /* this one cannot be static */
240
241         vlc_mutex_lock (&super_mutex);
242         assert (p_mutex->locked);
243         p_mutex->locked = false;
244         if (p_mutex->contention)
245             vlc_cond_broadcast (&super_variable);
246         vlc_mutex_unlock (&super_mutex);
247         return;
248     }
249
250     DosReleaseMutexSem( p_mutex->hmtx );
251 }
252
253 /*** Condition variables ***/
254 #undef CLOCK_REALTIME
255 #undef CLOCK_MONOTONIC
256 enum
257 {
258     CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
259     CLOCK_MONOTONIC,
260 };
261
262 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
263 {
264     /* Create a manual-reset event (manual reset is needed for broadcast). */
265     if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
266         abort();
267     p_condvar->clock = clock;
268 }
269
270 void vlc_cond_init (vlc_cond_t *p_condvar)
271 {
272     vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
273 }
274
275 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
276 {
277     vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
278 }
279
280 void vlc_cond_destroy (vlc_cond_t *p_condvar)
281 {
282     DosCloseEventSem( p_condvar->hev );
283 }
284
285 void vlc_cond_signal (vlc_cond_t *p_condvar)
286 {
287     if (!p_condvar->hev)
288         return;
289
290     /* This is suboptimal but works. */
291     vlc_cond_broadcast (p_condvar);
292 }
293
294 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
295 {
296     if (!p_condvar->hev)
297         return;
298
299     /* Wake all threads up (as the event HANDLE has manual reset) */
300     DosPostEventSem( p_condvar->hev );
301 }
302
303 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
304 {
305     ULONG ulPost;
306     ULONG rc;
307
308     if (!p_condvar->hev)
309     {   /* FIXME FIXME FIXME */
310         msleep (50000);
311         return;
312     }
313
314     do
315     {
316         vlc_testcancel();
317
318         vlc_mutex_unlock (p_mutex);
319         rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT );
320         vlc_mutex_lock (p_mutex);
321     } while( rc == ERROR_INTERRUPT );
322
323     DosResetEventSem( p_condvar->hev, &ulPost );
324 }
325
326 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
327                         mtime_t deadline)
328 {
329     ULONG   ulTimeout;
330     ULONG   ulPost;
331     ULONG   rc;
332
333     if (!p_condvar->hev)
334     {   /* FIXME FIXME FIXME */
335         msleep (50000);
336         return 0;
337     }
338
339     do
340     {
341         vlc_testcancel();
342
343         mtime_t total;
344         switch (p_condvar->clock)
345         {
346             case CLOCK_REALTIME: /* FIXME? sub-second precision */
347                 total = CLOCK_FREQ * time (NULL);
348                 break;
349             default:
350                 assert (p_condvar->clock == CLOCK_MONOTONIC);
351                 total = mdate();
352                 break;
353         }
354         total = (deadline - total) / 1000;
355         if( total < 0 )
356             total = 0;
357
358         ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
359
360         vlc_mutex_unlock (p_mutex);
361         rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
362         vlc_mutex_lock (p_mutex);
363     } while( rc == ERROR_INTERRUPT );
364
365     DosResetEventSem( p_condvar->hev, &ulPost );
366
367     return rc ? ETIMEDOUT : 0;
368 }
369
370 /*** Thread-specific variables (TLS) ***/
371 struct vlc_threadvar
372 {
373     PULONG                id;
374     void                (*destroy) (void *);
375     struct vlc_threadvar *prev;
376     struct vlc_threadvar *next;
377 } *vlc_threadvar_last = NULL;
378
379 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
380 {
381     ULONG rc;
382
383     struct vlc_threadvar *var = malloc (sizeof (*var));
384     if (unlikely(var == NULL))
385         return errno;
386
387     rc = DosAllocThreadLocalMemory( 1, &var->id );
388     if( rc )
389     {
390         free (var);
391         return EAGAIN;
392     }
393
394     var->destroy = destr;
395     var->next = NULL;
396     *p_tls = var;
397
398     vlc_mutex_lock (&super_mutex);
399     var->prev = vlc_threadvar_last;
400     if (var->prev)
401         var->prev->next = var;
402
403     vlc_threadvar_last = var;
404     vlc_mutex_unlock (&super_mutex);
405     return 0;
406 }
407
408 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
409 {
410     struct vlc_threadvar *var = *p_tls;
411
412     vlc_mutex_lock (&super_mutex);
413     if (var->prev != NULL)
414         var->prev->next = var->next;
415
416     if (var->next != NULL)
417         var->next->prev = var->prev;
418     else
419         vlc_threadvar_last = var->prev;
420
421     vlc_mutex_unlock (&super_mutex);
422
423     DosFreeThreadLocalMemory( var->id );
424     free (var);
425 }
426
427 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
428 {
429     *key->id = ( ULONG )value;
430     return 0;
431 }
432
433 void *vlc_threadvar_get (vlc_threadvar_t key)
434 {
435     return ( void * )*key->id;
436 }
437
438
439 /*** Threads ***/
440 void vlc_threads_setup (libvlc_int_t *p_libvlc)
441 {
442     (void) p_libvlc;
443 }
444
445 static void vlc_thread_cleanup (struct vlc_thread *th)
446 {
447     vlc_threadvar_t key;
448
449 retry:
450     /* TODO: use RW lock or something similar */
451     vlc_mutex_lock (&super_mutex);
452     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
453     {
454         void *value = vlc_threadvar_get (key);
455         if (value != NULL && key->destroy != NULL)
456         {
457             vlc_mutex_unlock (&super_mutex);
458             vlc_threadvar_set (key, NULL);
459             key->destroy (value);
460             goto retry;
461         }
462     }
463     vlc_mutex_unlock (&super_mutex);
464
465     if (th->detached)
466     {
467         DosCloseEventSem (th->cancel_event);
468         DosCloseEventSem (th->done_event );
469
470         soclose (th->cancel_sock);
471
472         free (th);
473     }
474 }
475
476 static void vlc_entry( void *p )
477 {
478     struct vlc_thread *th = p;
479
480     vlc_threadvar_set (thread_key, th);
481     th->killable = true;
482     th->data = th->entry (th->data);
483     DosPostEventSem( th->done_event );
484     vlc_thread_cleanup (th);
485 }
486
487 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
488                            void *(*entry) (void *), void *data, int priority)
489 {
490     struct vlc_thread *th = malloc (sizeof (*th));
491     if (unlikely(th == NULL))
492         return ENOMEM;
493     th->entry = entry;
494     th->data = data;
495     th->detached = detached;
496     th->killable = false; /* not until vlc_entry() ! */
497     th->killed = false;
498     th->cleaners = NULL;
499
500     if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
501         goto error;
502     if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
503         goto error;
504
505     th->cancel_sock = socket (AF_LOCAL, SOCK_STREAM, 0);
506     if( th->cancel_sock < 0 )
507         goto error;
508
509     th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
510     if((int)th->tid == -1)
511         goto error;
512
513     if (p_handle != NULL)
514         *p_handle = th;
515
516     if (priority)
517         DosSetPriority(PRTYS_THREAD,
518                        HIBYTE(priority),
519                        LOBYTE(priority),
520                        th->tid);
521
522     return 0;
523
524 error:
525     soclose (th->cancel_sock);
526     DosCloseEventSem (th->cancel_event);
527     DosCloseEventSem (th->done_event);
528     free (th);
529
530     return ENOMEM;
531 }
532
533 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
534                 void *data, int priority)
535 {
536     return vlc_clone_attr (p_handle, false, entry, data, priority);
537 }
538
539 void vlc_join (vlc_thread_t th, void **result)
540 {
541     ULONG rc;
542
543     do
544     {
545         vlc_testcancel();
546         rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
547     } while( rc == ERROR_INTERRUPT );
548
549     if (result != NULL)
550         *result = th->data;
551
552     DosCloseEventSem( th->cancel_event );
553     DosCloseEventSem( th->done_event );
554
555     soclose( th->cancel_sock );
556
557     free( th );
558 }
559
560 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
561                       void *data, int priority)
562 {
563     vlc_thread_t th;
564     if (p_handle == NULL)
565         p_handle = &th;
566
567     return vlc_clone_attr (p_handle, true, entry, data, priority);
568 }
569
570 int vlc_set_priority (vlc_thread_t th, int priority)
571 {
572     if (DosSetPriority(PRTYS_THREAD,
573                        HIBYTE(priority),
574                        LOBYTE(priority),
575                        th->tid))
576         return VLC_EGENERIC;
577     return VLC_SUCCESS;
578 }
579
580 /*** Thread cancellation ***/
581
582 /* APC procedure for thread cancellation */
583 static void vlc_cancel_self (PVOID self)
584 {
585     struct vlc_thread *th = self;
586
587     if (likely(th != NULL))
588         th->killed = true;
589 }
590
591 void vlc_cancel (vlc_thread_t thread_id)
592 {
593     DosPostEventSem( thread_id->cancel_event );
594     so_cancel( thread_id->cancel_sock );
595 }
596
597 int vlc_savecancel (void)
598 {
599     int state;
600
601     struct vlc_thread *th = vlc_threadvar_get (thread_key);
602     if (th == NULL)
603         return false; /* Main thread - cannot be cancelled anyway */
604
605     state = th->killable;
606     th->killable = false;
607     return state;
608 }
609
610 void vlc_restorecancel (int state)
611 {
612     struct vlc_thread *th = vlc_threadvar_get (thread_key);
613     assert (state == false || state == true);
614
615     if (th == NULL)
616         return; /* Main thread - cannot be cancelled anyway */
617
618     assert (!th->killable);
619     th->killable = state != 0;
620 }
621
622 void vlc_testcancel (void)
623 {
624     struct vlc_thread *th = vlc_threadvar_get (thread_key);
625     if (th == NULL)
626         return; /* Main thread - cannot be cancelled anyway */
627
628     /* This check is needed for the case that vlc_cancel() is followed by
629      * vlc_testcancel() without any cancellation point */
630     if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
631         vlc_cancel_self( th );
632
633     if (th->killable && th->killed)
634     {
635         for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
636              p->proc (p->data);
637
638         DosPostEventSem( th->done_event );
639         th->data = NULL; /* TODO: special value? */
640         vlc_thread_cleanup (th);
641         _endthread();
642     }
643 }
644
645 void vlc_control_cancel (int cmd, ...)
646 {
647     /* NOTE: This function only modifies thread-specific data, so there is no
648      * need to lock anything. */
649     va_list ap;
650
651     struct vlc_thread *th = vlc_threadvar_get (thread_key);
652     if (th == NULL)
653         return; /* Main thread - cannot be cancelled anyway */
654
655     va_start (ap, cmd);
656     switch (cmd)
657     {
658         case VLC_CLEANUP_PUSH:
659         {
660             /* cleaner is a pointer to the caller stack, no need to allocate
661              * and copy anything. As a nice side effect, this cannot fail. */
662             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
663             cleaner->next = th->cleaners;
664             th->cleaners = cleaner;
665             break;
666         }
667
668         case VLC_CLEANUP_POP:
669         {
670             th->cleaners = th->cleaners->next;
671             break;
672         }
673     }
674     va_end (ap);
675 }
676
677 static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
678                        struct timeval *timeout )
679 {
680     struct vlc_thread *th = vlc_threadvar_get( thread_key );
681
682     int rc;
683
684     if( th )
685     {
686         FD_SET( th->cancel_sock, rdset );
687
688         nfds = MAX( nfds, th->cancel_sock + 1 );
689     }
690
691     rc = select( nfds, rdset, wrset, exset, timeout );
692
693     vlc_testcancel();
694
695     return rc;
696
697 }
698
699 /* Export vlc_poll_os2 directly regardless of EXPORTS of .def */
700 __declspec(dllexport)
701 int vlc_poll_os2( struct pollfd *fds, unsigned nfds, int timeout )
702 {
703     fd_set rdset, wrset, exset;
704
705     struct timeval tv = { 0, 0 };
706
707     int val = -1;
708
709     FD_ZERO( &rdset );
710     FD_ZERO( &wrset );
711     FD_ZERO( &exset );
712     for( unsigned i = 0; i < nfds; i++ )
713     {
714         int fd = fds[ i ].fd;
715         if( val < fd )
716             val = fd;
717
718         if(( unsigned )fd >= FD_SETSIZE )
719         {
720             errno = EINVAL;
721             return -1;
722         }
723
724         if( fds[ i ].events & POLLIN )
725             FD_SET( fd, &rdset );
726         if( fds[ i ].events & POLLOUT )
727             FD_SET( fd, &wrset );
728         if( fds[ i ].events & POLLPRI )
729             FD_SET( fd, &exset );
730     }
731
732     if( timeout >= 0 )
733     {
734         div_t d    = div( timeout, 1000 );
735         tv.tv_sec  = d.quot;
736         tv.tv_usec = d.rem * 1000;
737     }
738
739     val = vlc_select( val + 1, &rdset, &wrset, &exset,
740                       ( timeout >= 0 ) ? &tv : NULL );
741     if( val == -1 )
742         return -1;
743
744     for( unsigned i = 0; i < nfds; i++ )
745     {
746         int fd = fds[ i ].fd;
747         fds[ i ].revents = ( FD_ISSET( fd, &rdset ) ? POLLIN  : 0 )
748                          | ( FD_ISSET( fd, &wrset ) ? POLLOUT : 0 )
749                          | ( FD_ISSET( fd, &exset ) ? POLLPRI : 0 );
750     }
751
752     return val;
753 }
754
755 #define Q2LL( q )   ( *( long long * )&( q ))
756
757 /*** Clock ***/
758 mtime_t mdate (void)
759 {
760     /* We don't need the real date, just the value of a high precision timer */
761     QWORD counter;
762     ULONG freq;
763     if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
764         abort();
765
766     /* Convert to from (1/freq) to microsecond resolution */
767     /* We need to split the division to avoid 63-bits overflow */
768     lldiv_t d = lldiv (Q2LL(counter), freq);
769
770     return (d.quot * 1000000) + ((d.rem * 1000000) / freq);
771 }
772
773 #undef mwait
774 void mwait (mtime_t deadline)
775 {
776     mtime_t delay;
777
778     vlc_testcancel();
779     while ((delay = (deadline - mdate())) > 0)
780     {
781         delay /= 1000;
782         if (unlikely(delay > 0x7fffffff))
783             delay = 0x7fffffff;
784         vlc_Sleep (delay);
785         vlc_testcancel();
786     }
787 }
788
789 #undef msleep
790 void msleep (mtime_t delay)
791 {
792     mwait (mdate () + delay);
793 }
794
795 /*** Timers ***/
796 struct vlc_timer
797 {
798     TID    tid;
799     HEV    hev;
800     HTIMER htimer;
801     ULONG  interval;
802     bool   quit;
803     void (*func) (void *);
804     void  *data;
805 };
806
807 static void vlc_timer_do (void *arg)
808 {
809     struct vlc_timer *timer = arg;
810
811     while (1)
812     {
813         ULONG count;
814
815         DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
816         DosResetEventSem (timer->hev, &count);
817
818         if (timer->quit)
819             break;
820
821         timer->func (timer->data);
822
823         if (timer->interval)
824             DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
825     }
826 }
827
828 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
829 {
830     struct vlc_timer *timer = malloc (sizeof (*timer));
831
832     if (timer == NULL)
833         return ENOMEM;
834
835     timer->func = func;
836     timer->data = data;
837
838     DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
839     timer->htimer = NULLHANDLE;
840     timer->interval = 0;
841     timer->quit = false;
842     timer->tid  = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
843
844     *id = timer;
845     return 0;
846 }
847
848 void vlc_timer_destroy (vlc_timer_t timer)
849 {
850     if (timer->htimer != NULLHANDLE)
851         DosStopTimer (timer->htimer);
852
853     timer->quit = true;
854     DosPostEventSem (timer->hev);
855     DosWaitThread (&timer->tid, DCWW_WAIT);
856     DosCloseEventSem (timer->hev);
857
858     free (timer);
859 }
860
861 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
862                          mtime_t value, mtime_t interval)
863 {
864     if (timer->htimer != NULLHANDLE)
865     {
866         DosStopTimer (timer->htimer);
867         timer->htimer = NULLHANDLE;
868         timer->interval = 0;
869     }
870
871     if (value == 0)
872         return; /* Disarm */
873
874     if (absolute)
875         value -= mdate ();
876     value = (value + 999) / 1000;
877     interval = (interval + 999) / 1000;
878
879     timer->interval = interval;
880     if (DosAsyncTimer (value, (HSEM)timer->hev, &timer->htimer))
881         abort ();
882 }
883
884 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
885 {
886     (void)timer;
887     return 0;
888 }
889
890 /*** CPU ***/
891 unsigned vlc_GetCPUCount (void)
892 {
893     ULONG numprocs = 1;
894
895     DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
896                     &numprocs, sizeof(numprocs));
897
898     return numprocs;
899 }