]> git.sesse.net Git - vlc/blob - src/os2/thread.c
ncurses: translate title
[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 int vlc_poll( struct pollfd *fds, unsigned nfds, int timeout )
700 {
701     fd_set rdset, wrset, exset;
702
703     struct timeval tv = { 0, 0 };
704
705     int val = -1;
706
707     FD_ZERO( &rdset );
708     FD_ZERO( &wrset );
709     FD_ZERO( &exset );
710     for( unsigned i = 0; i < nfds; i++ )
711     {
712         int fd = fds[ i ].fd;
713         if( val < fd )
714             val = fd;
715
716         if(( unsigned )fd >= FD_SETSIZE )
717         {
718             errno = EINVAL;
719             return -1;
720         }
721
722         if( fds[ i ].events & POLLIN )
723             FD_SET( fd, &rdset );
724         if( fds[ i ].events & POLLOUT )
725             FD_SET( fd, &wrset );
726         if( fds[ i ].events & POLLPRI )
727             FD_SET( fd, &exset );
728     }
729
730     if( timeout >= 0 )
731     {
732         div_t d    = div( timeout, 1000 );
733         tv.tv_sec  = d.quot;
734         tv.tv_usec = d.rem * 1000;
735     }
736
737     val = vlc_select( val + 1, &rdset, &wrset, &exset,
738                       ( timeout >= 0 ) ? &tv : NULL );
739     if( val == -1 )
740         return -1;
741
742     for( unsigned i = 0; i < nfds; i++ )
743     {
744         int fd = fds[ i ].fd;
745         fds[ i ].revents = ( FD_ISSET( fd, &rdset ) ? POLLIN  : 0 )
746                          | ( FD_ISSET( fd, &wrset ) ? POLLOUT : 0 )
747                          | ( FD_ISSET( fd, &exset ) ? POLLPRI : 0 );
748     }
749
750     return val;
751 }
752
753 #define Q2LL( q )   ( *( long long * )&( q ))
754
755 /*** Clock ***/
756 mtime_t mdate (void)
757 {
758     /* We don't need the real date, just the value of a high precision timer */
759     QWORD counter;
760     ULONG freq;
761     if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
762         abort();
763
764     /* Convert to from (1/freq) to microsecond resolution */
765     /* We need to split the division to avoid 63-bits overflow */
766     lldiv_t d = lldiv (Q2LL(counter), freq);
767
768     return (d.quot * 1000000) + ((d.rem * 1000000) / freq);
769 }
770
771 #undef mwait
772 void mwait (mtime_t deadline)
773 {
774     mtime_t delay;
775
776     vlc_testcancel();
777     while ((delay = (deadline - mdate())) > 0)
778     {
779         delay /= 1000;
780         if (unlikely(delay > 0x7fffffff))
781             delay = 0x7fffffff;
782         vlc_Sleep (delay);
783         vlc_testcancel();
784     }
785 }
786
787 #undef msleep
788 void msleep (mtime_t delay)
789 {
790     mwait (mdate () + delay);
791 }
792
793 /*** Timers ***/
794 struct vlc_timer
795 {
796     TID    tid;
797     HEV    hev;
798     HTIMER htimer;
799     ULONG  interval;
800     bool   quit;
801     void (*func) (void *);
802     void  *data;
803 };
804
805 static void vlc_timer_do (void *arg)
806 {
807     struct vlc_timer *timer = arg;
808
809     while (1)
810     {
811         ULONG count;
812
813         DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
814         DosResetEventSem (timer->hev, &count);
815
816         if (timer->quit)
817             break;
818
819         timer->func (timer->data);
820
821         if (timer->interval)
822             DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
823     }
824 }
825
826 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
827 {
828     struct vlc_timer *timer = malloc (sizeof (*timer));
829
830     if (timer == NULL)
831         return ENOMEM;
832
833     timer->func = func;
834     timer->data = data;
835
836     DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
837     timer->htimer = NULLHANDLE;
838     timer->interval = 0;
839     timer->quit = false;
840     timer->tid  = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
841
842     *id = timer;
843     return 0;
844 }
845
846 void vlc_timer_destroy (vlc_timer_t timer)
847 {
848     if (timer->htimer != NULLHANDLE)
849         DosStopTimer (timer->htimer);
850
851     timer->quit = true;
852     DosPostEventSem (timer->hev);
853     DosWaitThread (&timer->tid, DCWW_WAIT);
854     DosCloseEventSem (timer->hev);
855
856     free (timer);
857 }
858
859 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
860                          mtime_t value, mtime_t interval)
861 {
862     if (timer->htimer != NULLHANDLE)
863     {
864         DosStopTimer (timer->htimer);
865         timer->htimer = NULLHANDLE;
866         timer->interval = 0;
867     }
868
869     if (value == 0)
870         return; /* Disarm */
871
872     if (absolute)
873         value -= mdate ();
874     value = (value + 999) / 1000;
875     interval = (interval + 999) / 1000;
876
877     timer->interval = interval;
878     if (DosAsyncTimer (value, (HSEM)timer->hev, &timer->htimer))
879         abort ();
880 }
881
882 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
883 {
884     (void)timer;
885     return 0;
886 }
887
888 /*** CPU ***/
889 unsigned vlc_GetCPUCount (void)
890 {
891     ULONG numprocs = 1;
892
893     DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
894                     &numprocs, sizeof(numprocs));
895
896     return numprocs;
897 }