]> git.sesse.net Git - vlc/blob - src/os2/thread.c
os2: implement poll() with timeout free cancellation
[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, msg_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_rwlock_init (&msg_lock);
153             vlc_CPU_init ();
154
155             return 1;
156
157         case 1 :    /* Termination */
158             vlc_rwlock_destroy (&msg_lock);
159             vlc_rwlock_destroy (&config_lock);
160             vlc_threadvar_delete (&thread_key);
161             vlc_cond_destroy (&super_variable);
162             vlc_mutex_destroy (&super_mutex);
163
164             _CRT_term();
165
166             return 1;
167     }
168
169     return 0;   /* Failed */
170 }
171
172 /*** Mutexes ***/
173 void vlc_mutex_init( vlc_mutex_t *p_mutex )
174 {
175     /* This creates a recursive mutex. This is OK as fast mutexes have
176      * no defined behavior in case of recursive locking. */
177     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
178     p_mutex->dynamic = true;
179 }
180
181 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
182 {
183     DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
184     p_mutex->dynamic = true;
185 }
186
187
188 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
189 {
190     assert (p_mutex->dynamic);
191     DosCloseMutexSem( p_mutex->hmtx );
192 }
193
194 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
195 {
196     if (!p_mutex->dynamic)
197     {   /* static mutexes */
198         int canc = vlc_savecancel ();
199         assert (p_mutex != &super_mutex); /* this one cannot be static */
200
201         vlc_mutex_lock (&super_mutex);
202         while (p_mutex->locked)
203         {
204             p_mutex->contention++;
205             vlc_cond_wait (&super_variable, &super_mutex);
206             p_mutex->contention--;
207         }
208         p_mutex->locked = true;
209         vlc_mutex_unlock (&super_mutex);
210         vlc_restorecancel (canc);
211         return;
212     }
213
214     DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
215 }
216
217 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
218 {
219     if (!p_mutex->dynamic)
220     {   /* static mutexes */
221         int ret = EBUSY;
222
223         assert (p_mutex != &super_mutex); /* this one cannot be static */
224         vlc_mutex_lock (&super_mutex);
225         if (!p_mutex->locked)
226         {
227             p_mutex->locked = true;
228             ret = 0;
229         }
230         vlc_mutex_unlock (&super_mutex);
231         return ret;
232     }
233
234     return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
235 }
236
237 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
238 {
239     if (!p_mutex->dynamic)
240     {   /* static mutexes */
241         assert (p_mutex != &super_mutex); /* this one cannot be static */
242
243         vlc_mutex_lock (&super_mutex);
244         assert (p_mutex->locked);
245         p_mutex->locked = false;
246         if (p_mutex->contention)
247             vlc_cond_broadcast (&super_variable);
248         vlc_mutex_unlock (&super_mutex);
249         return;
250     }
251
252     DosReleaseMutexSem( p_mutex->hmtx );
253 }
254
255 /*** Condition variables ***/
256 #undef CLOCK_REALTIME
257 #undef CLOCK_MONOTONIC
258 enum
259 {
260     CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
261     CLOCK_MONOTONIC,
262 };
263
264 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
265 {
266     /* Create a manual-reset event (manual reset is needed for broadcast). */
267     if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
268         abort();
269     p_condvar->clock = clock;
270 }
271
272 void vlc_cond_init (vlc_cond_t *p_condvar)
273 {
274     vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
275 }
276
277 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
278 {
279     vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
280 }
281
282 void vlc_cond_destroy (vlc_cond_t *p_condvar)
283 {
284     DosCloseEventSem( p_condvar->hev );
285 }
286
287 void vlc_cond_signal (vlc_cond_t *p_condvar)
288 {
289     if (!p_condvar->hev)
290         return;
291
292     /* This is suboptimal but works. */
293     vlc_cond_broadcast (p_condvar);
294 }
295
296 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
297 {
298     if (!p_condvar->hev)
299         return;
300
301     /* Wake all threads up (as the event HANDLE has manual reset) */
302     DosPostEventSem( p_condvar->hev );
303 }
304
305 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
306 {
307     ULONG ulPost;
308     ULONG rc;
309
310     if (!p_condvar->hev)
311     {   /* FIXME FIXME FIXME */
312         msleep (50000);
313         return;
314     }
315
316     do
317     {
318         vlc_testcancel();
319
320         vlc_mutex_unlock (p_mutex);
321         rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT );
322         vlc_mutex_lock (p_mutex);
323     } while( rc == ERROR_INTERRUPT );
324
325     DosResetEventSem( p_condvar->hev, &ulPost );
326 }
327
328 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
329                         mtime_t deadline)
330 {
331     ULONG   ulTimeout;
332     ULONG   ulPost;
333     ULONG   rc;
334
335     if (!p_condvar->hev)
336     {   /* FIXME FIXME FIXME */
337         msleep (50000);
338         return 0;
339     }
340
341     do
342     {
343         vlc_testcancel();
344
345         mtime_t total;
346         switch (p_condvar->clock)
347         {
348             case CLOCK_REALTIME: /* FIXME? sub-second precision */
349                 total = CLOCK_FREQ * time (NULL);
350                 break;
351             default:
352                 assert (p_condvar->clock == CLOCK_MONOTONIC);
353                 total = mdate();
354                 break;
355         }
356         total = (deadline - total) / 1000;
357         if( total < 0 )
358             total = 0;
359
360         ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
361
362         vlc_mutex_unlock (p_mutex);
363         rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
364         vlc_mutex_lock (p_mutex);
365     } while( rc == ERROR_INTERRUPT );
366
367     DosResetEventSem( p_condvar->hev, &ulPost );
368
369     return rc ? ETIMEDOUT : 0;
370 }
371
372 /*** Thread-specific variables (TLS) ***/
373 struct vlc_threadvar
374 {
375     PULONG                id;
376     void                (*destroy) (void *);
377     struct vlc_threadvar *prev;
378     struct vlc_threadvar *next;
379 } *vlc_threadvar_last = NULL;
380
381 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
382 {
383     ULONG rc;
384
385     struct vlc_threadvar *var = malloc (sizeof (*var));
386     if (unlikely(var == NULL))
387         return errno;
388
389     rc = DosAllocThreadLocalMemory( 1, &var->id );
390     if( rc )
391     {
392         free (var);
393         return EAGAIN;
394     }
395
396     var->destroy = destr;
397     var->next = NULL;
398     *p_tls = var;
399
400     vlc_mutex_lock (&super_mutex);
401     var->prev = vlc_threadvar_last;
402     if (var->prev)
403         var->prev->next = var;
404
405     vlc_threadvar_last = var;
406     vlc_mutex_unlock (&super_mutex);
407     return 0;
408 }
409
410 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
411 {
412     struct vlc_threadvar *var = *p_tls;
413
414     vlc_mutex_lock (&super_mutex);
415     if (var->prev != NULL)
416         var->prev->next = var->next;
417
418     if (var->next != NULL)
419         var->next->prev = var->prev;
420     else
421         vlc_threadvar_last = var->prev;
422
423     vlc_mutex_unlock (&super_mutex);
424
425     DosFreeThreadLocalMemory( var->id );
426     free (var);
427 }
428
429 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
430 {
431     *key->id = ( ULONG )value;
432     return 0;
433 }
434
435 void *vlc_threadvar_get (vlc_threadvar_t key)
436 {
437     return ( void * )*key->id;
438 }
439
440
441 /*** Threads ***/
442 void vlc_threads_setup (libvlc_int_t *p_libvlc)
443 {
444     (void) p_libvlc;
445 }
446
447 static void vlc_thread_cleanup (struct vlc_thread *th)
448 {
449     vlc_threadvar_t key;
450
451 retry:
452     /* TODO: use RW lock or something similar */
453     vlc_mutex_lock (&super_mutex);
454     for (key = vlc_threadvar_last; key != NULL; key = key->prev)
455     {
456         void *value = vlc_threadvar_get (key);
457         if (value != NULL && key->destroy != NULL)
458         {
459             vlc_mutex_unlock (&super_mutex);
460             vlc_threadvar_set (key, NULL);
461             key->destroy (value);
462             goto retry;
463         }
464     }
465     vlc_mutex_unlock (&super_mutex);
466
467     if (th->detached)
468     {
469         DosCloseEventSem (th->cancel_event);
470         DosCloseEventSem (th->done_event );
471
472         soclose (th->cancel_sock);
473
474         free (th);
475     }
476 }
477
478 static void vlc_entry( void *p )
479 {
480     struct vlc_thread *th = p;
481
482     vlc_threadvar_set (thread_key, th);
483     th->killable = true;
484     th->data = th->entry (th->data);
485     DosPostEventSem( th->done_event );
486     vlc_thread_cleanup (th);
487 }
488
489 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
490                            void *(*entry) (void *), void *data, int priority)
491 {
492     struct vlc_thread *th = malloc (sizeof (*th));
493     if (unlikely(th == NULL))
494         return ENOMEM;
495     th->entry = entry;
496     th->data = data;
497     th->detached = detached;
498     th->killable = false; /* not until vlc_entry() ! */
499     th->killed = false;
500     th->cleaners = NULL;
501
502     if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
503         goto error;
504     if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
505         goto error;
506
507     th->cancel_sock = socket (AF_LOCAL, SOCK_STREAM, 0);
508     if( th->cancel_sock < 0 )
509         goto error;
510
511     th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
512     if((int)th->tid == -1)
513         goto error;
514
515     if (p_handle != NULL)
516         *p_handle = th;
517
518     if (priority)
519         DosSetPriority(PRTYS_THREAD,
520                        HIBYTE(priority),
521                        LOBYTE(priority),
522                        th->tid);
523
524     return 0;
525
526 error:
527     soclose (th->cancel_sock);
528     DosCloseEventSem (th->cancel_event);
529     DosCloseEventSem (th->done_event);
530     free (th);
531
532     return ENOMEM;
533 }
534
535 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
536                 void *data, int priority)
537 {
538     return vlc_clone_attr (p_handle, false, entry, data, priority);
539 }
540
541 void vlc_join (vlc_thread_t th, void **result)
542 {
543     ULONG rc;
544
545     do
546     {
547         vlc_testcancel();
548         rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
549     } while( rc == ERROR_INTERRUPT );
550
551     if (result != NULL)
552         *result = th->data;
553
554     DosCloseEventSem( th->cancel_event );
555     DosCloseEventSem( th->done_event );
556
557     soclose( th->cancel_sock );
558
559     free( th );
560 }
561
562 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
563                       void *data, int priority)
564 {
565     vlc_thread_t th;
566     if (p_handle == NULL)
567         p_handle = &th;
568
569     return vlc_clone_attr (p_handle, true, entry, data, priority);
570 }
571
572 int vlc_set_priority (vlc_thread_t th, int priority)
573 {
574     if (DosSetPriority(PRTYS_THREAD,
575                        HIBYTE(priority),
576                        LOBYTE(priority),
577                        th->tid))
578         return VLC_EGENERIC;
579     return VLC_SUCCESS;
580 }
581
582 /*** Thread cancellation ***/
583
584 /* APC procedure for thread cancellation */
585 static void vlc_cancel_self (PVOID self)
586 {
587     struct vlc_thread *th = self;
588
589     if (likely(th != NULL))
590         th->killed = true;
591 }
592
593 void vlc_cancel (vlc_thread_t thread_id)
594 {
595     DosPostEventSem( thread_id->cancel_event );
596     so_cancel( thread_id->cancel_sock );
597 }
598
599 int vlc_savecancel (void)
600 {
601     int state;
602
603     struct vlc_thread *th = vlc_threadvar_get (thread_key);
604     if (th == NULL)
605         return false; /* Main thread - cannot be cancelled anyway */
606
607     state = th->killable;
608     th->killable = false;
609     return state;
610 }
611
612 void vlc_restorecancel (int state)
613 {
614     struct vlc_thread *th = vlc_threadvar_get (thread_key);
615     assert (state == false || state == true);
616
617     if (th == NULL)
618         return; /* Main thread - cannot be cancelled anyway */
619
620     assert (!th->killable);
621     th->killable = state != 0;
622 }
623
624 void vlc_testcancel (void)
625 {
626     struct vlc_thread *th = vlc_threadvar_get (thread_key);
627     if (th == NULL)
628         return; /* Main thread - cannot be cancelled anyway */
629
630     /* This check is needed for the case that vlc_cancel() is followed by
631      * vlc_testcancel() without any cancellation point */
632     if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
633         vlc_cancel_self( th );
634
635     if (th->killable && th->killed)
636     {
637         for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
638              p->proc (p->data);
639
640         DosPostEventSem( th->done_event );
641         th->data = NULL; /* TODO: special value? */
642         vlc_thread_cleanup (th);
643         _endthread();
644     }
645 }
646
647 void vlc_control_cancel (int cmd, ...)
648 {
649     /* NOTE: This function only modifies thread-specific data, so there is no
650      * need to lock anything. */
651     va_list ap;
652
653     struct vlc_thread *th = vlc_threadvar_get (thread_key);
654     if (th == NULL)
655         return; /* Main thread - cannot be cancelled anyway */
656
657     va_start (ap, cmd);
658     switch (cmd)
659     {
660         case VLC_CLEANUP_PUSH:
661         {
662             /* cleaner is a pointer to the caller stack, no need to allocate
663              * and copy anything. As a nice side effect, this cannot fail. */
664             vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
665             cleaner->next = th->cleaners;
666             th->cleaners = cleaner;
667             break;
668         }
669
670         case VLC_CLEANUP_POP:
671         {
672             th->cleaners = th->cleaners->next;
673             break;
674         }
675     }
676     va_end (ap);
677 }
678
679 static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
680                        struct timeval *timeout )
681 {
682     struct vlc_thread *th = vlc_threadvar_get( thread_key );
683
684     int rc;
685
686     if( th )
687     {
688         FD_SET( th->cancel_sock, rdset );
689
690         nfds = MAX( nfds, th->cancel_sock + 1 );
691     }
692
693     rc = select( nfds, rdset, wrset, exset, timeout );
694
695     vlc_testcancel();
696
697     return rc;
698
699 }
700
701 int vlc_poll( 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 }