Revert "input: Allow track autoselection to be enabled/disabled"
[vlc.git] / src / os2 / thread.c
blobb732e9fb6e11a43bb1f281267a3975dd515e3479
1 /*****************************************************************************
2 * thread.c : OS/2 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2011 VLC authors and VideoLAN
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
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.
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.
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 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
35 #include "libvlc.h"
36 #include <stdarg.h>
37 #include <assert.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <time.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
47 #include <sys/time.h>
48 #include <sys/select.h>
50 #include <sys/builtin.h>
52 #include <sys/stat.h>
54 static vlc_threadvar_t thread_key;
56 struct vlc_thread
58 TID tid;
59 HEV cancel_event;
60 HEV done_event;
61 int cancel_sock;
63 bool detached;
64 bool killable;
65 bool killed;
66 vlc_cleanup_t *cleaners;
68 void *(*entry) (void *);
69 void *data;
72 static void vlc_cancel_self (PVOID dummy);
74 static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout )
76 HMUX hmux;
77 SEMRECORD asr[ 2 ];
78 ULONG ulUser;
79 int n;
80 ULONG rc;
82 struct vlc_thread *th = vlc_threadvar_get(thread_key);
83 if( th == NULL || !th->killable )
85 /* Main thread - cannot be cancelled anyway
86 * Alien thread - out of our control
87 * Cancel disabled thread - ignore cancel
89 if( hev != NULLHANDLE )
90 return DosWaitEventSem( hev, ulTimeout );
92 return DosSleep( ulTimeout );
95 n = 0;
96 if( hev != NULLHANDLE )
98 asr[ n ].hsemCur = ( HSEM )hev;
99 asr[ n ].ulUser = 0;
100 n++;
102 asr[ n ].hsemCur = ( HSEM )th->cancel_event;
103 asr[ n ].ulUser = 0xFFFF;
104 n++;
106 DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
107 rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
108 DosCloseMuxWaitSem( hmux );
109 if( rc )
110 return rc;
112 if( ulUser == 0xFFFF )
114 vlc_cancel_self( th );
115 return ERROR_INTERRUPT;
118 return NO_ERROR;
121 static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
123 return vlc_DosWaitEventSemEx( hev, ulTimeout );
126 static ULONG vlc_Sleep (ULONG ulTimeout)
128 ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout );
130 return ( rc != ERROR_TIMEOUT ) ? rc : 0;
133 static vlc_mutex_t super_mutex;
134 static vlc_cond_t super_variable;
135 extern vlc_rwlock_t config_lock;
137 int _CRT_init(void);
138 void _CRT_term(void);
140 unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
142 unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
144 VLC_UNUSED (hmod);
146 switch (flag)
148 case 0 : /* Initialization */
149 if(_CRT_init() == -1)
150 return 0;
152 vlc_mutex_init (&super_mutex);
153 vlc_cond_init (&super_variable);
154 vlc_threadvar_create (&thread_key, NULL);
155 vlc_rwlock_init (&config_lock);
157 return 1;
159 case 1 : /* Termination */
160 vlc_rwlock_destroy (&config_lock);
161 vlc_threadvar_delete (&thread_key);
163 _CRT_term();
165 return 1;
168 return 0; /* Failed */
171 void vlc_once(vlc_once_t *once, void (*cb)(void))
173 unsigned done;
175 /* load once->done */
176 __atomic_xchg( &done, once->done );
178 /* not initialized ? */
179 if( done == 0 )
181 vlc_mutex_lock( &once->mutex );
183 /* load once->done */
184 __atomic_xchg( &done, once->done );
186 /* still not initialized ? */
187 if( done == 0 )
189 cb();
191 /* set once->done to 1 */
192 __atomic_xchg( &once->done, 1 );
195 vlc_mutex_unlock( &once->mutex );
199 /*** Thread-specific variables (TLS) ***/
200 struct vlc_threadvar
202 PULONG id;
203 void (*destroy) (void *);
204 struct vlc_threadvar *prev;
205 struct vlc_threadvar *next;
206 } *vlc_threadvar_last = NULL;
208 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
210 ULONG rc;
212 struct vlc_threadvar *var = malloc (sizeof (*var));
213 if (unlikely(var == NULL))
214 return errno;
216 rc = DosAllocThreadLocalMemory( 1, &var->id );
217 if( rc )
219 free (var);
220 return EAGAIN;
223 var->destroy = destr;
224 var->next = NULL;
225 *p_tls = var;
227 vlc_mutex_lock (&super_mutex);
228 var->prev = vlc_threadvar_last;
229 if (var->prev)
230 var->prev->next = var;
232 vlc_threadvar_last = var;
233 vlc_mutex_unlock (&super_mutex);
234 return 0;
237 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
239 struct vlc_threadvar *var = *p_tls;
241 vlc_mutex_lock (&super_mutex);
242 if (var->prev != NULL)
243 var->prev->next = var->next;
245 if (var->next != NULL)
246 var->next->prev = var->prev;
247 else
248 vlc_threadvar_last = var->prev;
250 vlc_mutex_unlock (&super_mutex);
252 DosFreeThreadLocalMemory( var->id );
253 free (var);
256 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
258 *key->id = ( ULONG )value;
259 return 0;
262 void *vlc_threadvar_get (vlc_threadvar_t key)
264 return ( void * )*key->id;
268 /*** Threads ***/
269 void vlc_threads_setup (libvlc_int_t *p_libvlc)
271 (void) p_libvlc;
274 static void vlc_thread_cleanup (struct vlc_thread *th)
276 vlc_threadvar_t key;
278 retry:
279 /* TODO: use RW lock or something similar */
280 vlc_mutex_lock (&super_mutex);
281 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
283 void *value = vlc_threadvar_get (key);
284 if (value != NULL && key->destroy != NULL)
286 vlc_mutex_unlock (&super_mutex);
287 vlc_threadvar_set (key, NULL);
288 key->destroy (value);
289 goto retry;
292 vlc_mutex_unlock (&super_mutex);
294 if (th->detached)
296 DosCloseEventSem (th->cancel_event);
297 DosCloseEventSem (th->done_event );
299 soclose (th->cancel_sock);
301 free (th);
305 static void vlc_entry( void *p )
307 struct vlc_thread *th = p;
309 vlc_threadvar_set (thread_key, th);
310 th->killable = true;
311 th->data = th->entry (th->data);
312 DosPostEventSem( th->done_event );
313 vlc_thread_cleanup (th);
316 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
317 void *(*entry) (void *), void *data, int priority)
319 struct vlc_thread *th = malloc (sizeof (*th));
320 if (unlikely(th == NULL))
321 return ENOMEM;
322 th->entry = entry;
323 th->data = data;
324 th->detached = detached;
325 th->killable = false; /* not until vlc_entry() ! */
326 th->killed = false;
327 th->cleaners = NULL;
329 if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
330 goto error;
331 if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
332 goto error;
334 th->cancel_sock = socket (AF_LOCAL, SOCK_STREAM, 0);
335 if( th->cancel_sock < 0 )
336 goto error;
338 th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
339 if((int)th->tid == -1)
340 goto error;
342 if (p_handle != NULL)
343 *p_handle = th;
345 if (priority)
346 DosSetPriority(PRTYS_THREAD,
347 HIBYTE(priority),
348 LOBYTE(priority),
349 th->tid);
351 return 0;
353 error:
354 soclose (th->cancel_sock);
355 DosCloseEventSem (th->cancel_event);
356 DosCloseEventSem (th->done_event);
357 free (th);
359 return ENOMEM;
362 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
363 void *data, int priority)
365 return vlc_clone_attr (p_handle, false, entry, data, priority);
368 void vlc_join (vlc_thread_t th, void **result)
370 ULONG rc;
374 vlc_testcancel();
375 rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
376 } while( rc == ERROR_INTERRUPT );
378 if (result != NULL)
379 *result = th->data;
381 DosCloseEventSem( th->cancel_event );
382 DosCloseEventSem( th->done_event );
384 soclose( th->cancel_sock );
386 free( th );
389 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
390 void *data, int priority)
392 vlc_thread_t th;
393 if (p_handle == NULL)
394 p_handle = &th;
396 return vlc_clone_attr (p_handle, true, entry, data, priority);
399 int vlc_set_priority (vlc_thread_t th, int priority)
401 if (DosSetPriority(PRTYS_THREAD,
402 HIBYTE(priority),
403 LOBYTE(priority),
404 th->tid))
405 return VLC_EGENERIC;
406 return VLC_SUCCESS;
409 unsigned long vlc_thread_self(void)
411 return vlc_thread_id();
414 unsigned long vlc_thread_id (void)
416 return _gettid();
419 /*** Thread cancellation ***/
421 /* APC procedure for thread cancellation */
422 static void vlc_cancel_self (PVOID self)
424 struct vlc_thread *th = self;
426 if (likely(th != NULL))
427 th->killed = true;
430 void vlc_cancel (vlc_thread_t thread_id)
432 DosPostEventSem( thread_id->cancel_event );
433 so_cancel( thread_id->cancel_sock );
436 int vlc_savecancel (void)
438 int state;
440 struct vlc_thread *th = vlc_threadvar_get(thread_key);
441 if (th == NULL)
442 return false; /* Main thread - cannot be cancelled anyway */
444 state = th->killable;
445 th->killable = false;
446 return state;
449 void vlc_restorecancel (int state)
451 struct vlc_thread *th = vlc_threadvar_get(thread_key);
452 assert (state == false || state == true);
454 if (th == NULL)
455 return; /* Main thread - cannot be cancelled anyway */
457 assert (!th->killable);
458 th->killable = state != 0;
461 void vlc_testcancel (void)
463 struct vlc_thread *th = vlc_threadvar_get(thread_key);
464 if (th == NULL)
465 return; /* Main thread - cannot be cancelled anyway */
467 /* This check is needed for the case that vlc_cancel() is followed by
468 * vlc_testcancel() without any cancellation point */
469 if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
470 vlc_cancel_self( th );
472 if (th->killable && th->killed)
474 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
475 p->proc (p->data);
477 DosPostEventSem( th->done_event );
478 th->data = NULL; /* TODO: special value? */
479 vlc_thread_cleanup (th);
480 _endthread();
484 void vlc_control_cancel (vlc_cleanup_t *cleaner)
486 /* NOTE: This function only modifies thread-specific data, so there is no
487 * need to lock anything. */
489 struct vlc_thread *th = vlc_threadvar_get(thread_key);
490 if (th == NULL)
491 return; /* Main thread - cannot be cancelled anyway */
493 if (cleaner != NULL)
495 /* cleaner is a pointer to the caller stack, no need to allocate
496 * and copy anything. As a nice side effect, this cannot fail. */
497 cleaner->next = th->cleaners;
498 th->cleaners = cleaner;
500 else
502 th->cleaners = th->cleaners->next;
506 static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
507 struct timeval *timeout )
509 struct vlc_thread *th = vlc_threadvar_get(thread_key);
511 int rc;
513 if( th )
515 FD_SET( th->cancel_sock, rdset );
517 nfds = MAX( nfds, th->cancel_sock + 1 );
520 rc = select( nfds, rdset, wrset, exset, timeout );
522 vlc_testcancel();
524 return rc;
528 /* Export vlc_poll_os2 directly regardless of EXPORTS of .def */
529 __declspec(dllexport)
530 int vlc_poll_os2( struct pollfd *fds, unsigned nfds, int timeout );
532 __declspec(dllexport)
533 int vlc_poll_os2( struct pollfd *fds, unsigned nfds, int timeout )
535 fd_set rdset, wrset, exset;
537 int non_sockets = 0;
539 struct timeval tv = { 0, 0 };
541 int val = -1;
543 FD_ZERO( &rdset );
544 FD_ZERO( &wrset );
545 FD_ZERO( &exset );
546 for( unsigned i = 0; i < nfds; i++ )
548 int fd = fds[ i ].fd;
549 struct stat stbuf;
551 fds[ i ].revents = 0;
553 if( fstat( fd, &stbuf ) == -1 ||
554 (errno = 0, !S_ISSOCK( stbuf.st_mode )))
556 if( fd >= 0 )
558 /* If regular files, assume readiness for requested modes */
559 fds[ i ].revents = ( !errno && S_ISREG( stbuf.st_mode ))
560 ? ( fds[ i ].events &
561 ( POLLIN | POLLOUT | POLLPRI ))
562 : POLLNVAL;
564 non_sockets++;
567 continue;
570 if( val < fd )
571 val = fd;
573 if(( unsigned )fd >= FD_SETSIZE )
575 errno = EINVAL;
576 return -1;
579 if( fds[ i ].events & POLLIN )
580 FD_SET( fd, &rdset );
581 if( fds[ i ].events & POLLOUT )
582 FD_SET( fd, &wrset );
583 if( fds[ i ].events & POLLPRI )
584 FD_SET( fd, &exset );
587 if( non_sockets > 0 )
588 timeout = 0; /* Just check pending sockets */
590 /* Sockets included ? */
591 if( val != -1)
593 struct timeval *ptv = NULL;
595 if( timeout >= 0 )
597 div_t d = div( timeout, 1000 );
598 tv.tv_sec = d.quot;
599 tv.tv_usec = d.rem * 1000;
601 ptv = &tv;
604 if (vlc_select( val + 1, &rdset, &wrset, &exset, ptv ) == -1)
605 return -1;
608 val = 0;
609 for( unsigned i = 0; i < nfds; i++ )
611 int fd = fds[ i ].fd;
613 if( fd >= 0 && fds[ i ].revents == 0 )
615 fds[ i ].revents = ( FD_ISSET( fd, &rdset ) ? POLLIN : 0 )
616 | ( FD_ISSET( fd, &wrset ) ? POLLOUT : 0 )
617 | ( FD_ISSET( fd, &exset ) ? POLLPRI : 0 );
620 if( fds[ i ].revents != 0 )
621 val++;
624 return val;
627 #define Q2LL( q ) ( *( long long * )&( q ))
629 /*** Clock ***/
630 vlc_tick_t vlc_tick_now (void)
632 /* We don't need the real date, just the value of a high precision timer */
633 QWORD counter;
634 ULONG freq;
635 if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
636 abort();
638 /* Convert to from (1/freq) to microsecond resolution */
639 /* We need to split the division to avoid 63-bits overflow */
640 lldiv_t d = lldiv (Q2LL(counter), freq);
642 return vlc_tick_from_sec( d.quot ) + vlc_tick_from_samples(d.rem, freq);
645 #undef vlc_tick_wait
646 void vlc_tick_wait (vlc_tick_t deadline)
648 vlc_tick_t delay;
650 vlc_testcancel();
651 while ((delay = (deadline - vlc_tick_now())) > 0)
653 delay /= 1000;
654 if (unlikely(delay > 0x7fffffff))
655 delay = 0x7fffffff;
656 vlc_Sleep (delay);
657 vlc_testcancel();
661 #undef vlc_tick_sleep
662 void vlc_tick_sleep (vlc_tick_t delay)
664 vlc_tick_wait (vlc_tick_now () + delay);
667 /*** Timers ***/
668 struct vlc_timer
670 TID tid;
671 HEV hev;
672 HTIMER htimer;
673 ULONG interval;
674 bool quit;
675 void (*func) (void *);
676 void *data;
679 static void vlc_timer_do (void *arg)
681 struct vlc_timer *timer = arg;
683 while (1)
685 ULONG count;
687 DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
688 DosResetEventSem (timer->hev, &count);
690 if (timer->quit)
691 break;
693 timer->func (timer->data);
695 if (timer->interval)
696 DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
700 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
702 struct vlc_timer *timer = malloc (sizeof (*timer));
704 if (timer == NULL)
705 return ENOMEM;
707 timer->func = func;
708 timer->data = data;
710 DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
711 timer->htimer = NULLHANDLE;
712 timer->interval = 0;
713 timer->quit = false;
714 timer->tid = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
716 *id = timer;
717 return 0;
720 void vlc_timer_destroy (vlc_timer_t timer)
722 if (timer->htimer != NULLHANDLE)
723 DosStopTimer (timer->htimer);
725 timer->quit = true;
726 DosPostEventSem (timer->hev);
727 DosWaitThread (&timer->tid, DCWW_WAIT);
728 DosCloseEventSem (timer->hev);
730 free (timer);
733 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
734 vlc_tick_t value, vlc_tick_t interval)
736 if (timer->htimer != NULLHANDLE)
738 DosStopTimer (timer->htimer);
739 timer->htimer = NULLHANDLE;
740 timer->interval = 0;
743 if (value == VLC_TIMER_DISARM)
744 return; /* Disarm */
746 if (absolute)
747 value -= vlc_tick_now ();
748 value = (value + 999) / 1000;
749 interval = (interval + 999) / 1000;
751 timer->interval = MS_FROM_VLC_TICK(interval);
752 if (DosAsyncTimer (MS_FROM_VLC_TICK(value), (HSEM)timer->hev, &timer->htimer))
753 abort ();
756 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
758 (void)timer;
759 return 0;
762 /*** CPU ***/
763 unsigned vlc_GetCPUCount (void)
765 ULONG numprocs = 1;
767 DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
768 &numprocs, sizeof(numprocs));
770 return numprocs;