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>
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 *****************************************************************************/
33 #include <vlc_common.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
46 #include <sys/select.h>
48 static vlc_threadvar_t thread_key;
63 vlc_cleanup_t *cleaners;
65 void *(*entry) (void *);
69 static void vlc_cancel_self (PVOID dummy);
71 static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout, BOOL fCancelable )
79 struct vlc_thread *th = vlc_threadvar_get( thread_key );
80 if( th == NULL || !fCancelable )
82 /* Main thread - cannot be cancelled anyway
83 * Alien thread - out of our control
85 if( hev != NULLHANDLE )
86 return DosWaitEventSem( hev, ulTimeout );
88 return DosSleep( ulTimeout );
92 if( hev != NULLHANDLE )
94 asr[ n ].hsemCur = ( HSEM )hev;
98 asr[ n ].hsemCur = ( HSEM )th->cancel_event;
99 asr[ n ].ulUser = 0xFFFF;
102 DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
103 rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
104 DosCloseMuxWaitSem( hmux );
108 if( ulUser == 0xFFFF )
110 vlc_cancel_self( th );
111 return ERROR_INTERRUPT;
117 static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
119 return vlc_DosWaitEventSemEx( hev, ulTimeout, TRUE );
122 static ULONG vlc_Sleep (ULONG ulTimeout)
124 ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout, TRUE );
126 return ( rc != ERROR_TIMEOUT ) ? rc : 0;
129 static vlc_mutex_t super_mutex;
130 static vlc_cond_t super_variable;
131 extern vlc_rwlock_t config_lock, msg_lock;
134 void _CRT_term(void);
136 unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
138 unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
144 case 0 : /* Initialization */
145 if(_CRT_init() == -1)
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);
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);
169 return 0; /* Failed */
173 void vlc_mutex_init( vlc_mutex_t *p_mutex )
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;
181 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
183 DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
184 p_mutex->dynamic = true;
188 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
190 assert (p_mutex->dynamic);
191 DosCloseMutexSem( p_mutex->hmtx );
194 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
196 if (!p_mutex->dynamic)
197 { /* static mutexes */
198 int canc = vlc_savecancel ();
199 assert (p_mutex != &super_mutex); /* this one cannot be static */
201 vlc_mutex_lock (&super_mutex);
202 while (p_mutex->locked)
204 p_mutex->contention++;
205 vlc_cond_wait (&super_variable, &super_mutex);
206 p_mutex->contention--;
208 p_mutex->locked = true;
209 vlc_mutex_unlock (&super_mutex);
210 vlc_restorecancel (canc);
214 DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
217 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
219 if (!p_mutex->dynamic)
220 { /* static mutexes */
223 assert (p_mutex != &super_mutex); /* this one cannot be static */
224 vlc_mutex_lock (&super_mutex);
225 if (!p_mutex->locked)
227 p_mutex->locked = true;
230 vlc_mutex_unlock (&super_mutex);
234 return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
237 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
239 if (!p_mutex->dynamic)
240 { /* static mutexes */
241 assert (p_mutex != &super_mutex); /* this one cannot be static */
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);
252 DosReleaseMutexSem( p_mutex->hmtx );
255 /*** Condition variables ***/
256 #undef CLOCK_REALTIME
257 #undef CLOCK_MONOTONIC
260 CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
264 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
266 /* Create a manual-reset event (manual reset is needed for broadcast). */
267 if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
269 p_condvar->clock = clock;
272 void vlc_cond_init (vlc_cond_t *p_condvar)
274 vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
277 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
279 vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
282 void vlc_cond_destroy (vlc_cond_t *p_condvar)
284 DosCloseEventSem( p_condvar->hev );
287 void vlc_cond_signal (vlc_cond_t *p_condvar)
292 /* This is suboptimal but works. */
293 vlc_cond_broadcast (p_condvar);
296 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
301 /* Wake all threads up (as the event HANDLE has manual reset) */
302 DosPostEventSem( p_condvar->hev );
305 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
311 { /* FIXME FIXME FIXME */
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 );
325 DosResetEventSem( p_condvar->hev, &ulPost );
328 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
336 { /* FIXME FIXME FIXME */
346 switch (p_condvar->clock)
348 case CLOCK_REALTIME: /* FIXME? sub-second precision */
349 total = CLOCK_FREQ * time (NULL);
352 assert (p_condvar->clock == CLOCK_MONOTONIC);
356 total = (deadline - total) / 1000;
360 ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
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 );
367 DosResetEventSem( p_condvar->hev, &ulPost );
369 return rc ? ETIMEDOUT : 0;
372 /*** Thread-specific variables (TLS) ***/
376 void (*destroy) (void *);
377 struct vlc_threadvar *prev;
378 struct vlc_threadvar *next;
379 } *vlc_threadvar_last = NULL;
381 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
385 struct vlc_threadvar *var = malloc (sizeof (*var));
386 if (unlikely(var == NULL))
389 rc = DosAllocThreadLocalMemory( 1, &var->id );
396 var->destroy = destr;
400 vlc_mutex_lock (&super_mutex);
401 var->prev = vlc_threadvar_last;
403 var->prev->next = var;
405 vlc_threadvar_last = var;
406 vlc_mutex_unlock (&super_mutex);
410 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
412 struct vlc_threadvar *var = *p_tls;
414 vlc_mutex_lock (&super_mutex);
415 if (var->prev != NULL)
416 var->prev->next = var->next;
418 if (var->next != NULL)
419 var->next->prev = var->prev;
421 vlc_threadvar_last = var->prev;
423 vlc_mutex_unlock (&super_mutex);
425 DosFreeThreadLocalMemory( var->id );
429 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
431 *key->id = ( ULONG )value;
435 void *vlc_threadvar_get (vlc_threadvar_t key)
437 return ( void * )*key->id;
442 void vlc_threads_setup (libvlc_int_t *p_libvlc)
447 static void vlc_thread_cleanup (struct vlc_thread *th)
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)
456 void *value = vlc_threadvar_get (key);
457 if (value != NULL && key->destroy != NULL)
459 vlc_mutex_unlock (&super_mutex);
460 vlc_threadvar_set (key, NULL);
461 key->destroy (value);
465 vlc_mutex_unlock (&super_mutex);
469 DosCloseEventSem (th->cancel_event);
470 DosCloseEventSem (th->done_event );
472 soclose (th->cancel_sock);
478 static void vlc_entry( void *p )
480 struct vlc_thread *th = p;
482 vlc_threadvar_set (thread_key, th);
484 th->data = th->entry (th->data);
485 DosPostEventSem( th->done_event );
486 vlc_thread_cleanup (th);
489 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
490 void *(*entry) (void *), void *data, int priority)
492 struct vlc_thread *th = malloc (sizeof (*th));
493 if (unlikely(th == NULL))
497 th->detached = detached;
498 th->killable = false; /* not until vlc_entry() ! */
502 if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
504 if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
507 th->cancel_sock = socket (AF_LOCAL, SOCK_STREAM, 0);
508 if( th->cancel_sock < 0 )
511 th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
512 if((int)th->tid == -1)
515 if (p_handle != NULL)
519 DosSetPriority(PRTYS_THREAD,
527 soclose (th->cancel_sock);
528 DosCloseEventSem (th->cancel_event);
529 DosCloseEventSem (th->done_event);
535 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
536 void *data, int priority)
538 return vlc_clone_attr (p_handle, false, entry, data, priority);
541 void vlc_join (vlc_thread_t th, void **result)
548 rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
549 } while( rc == ERROR_INTERRUPT );
554 DosCloseEventSem( th->cancel_event );
555 DosCloseEventSem( th->done_event );
557 soclose( th->cancel_sock );
562 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
563 void *data, int priority)
566 if (p_handle == NULL)
569 return vlc_clone_attr (p_handle, true, entry, data, priority);
572 int vlc_set_priority (vlc_thread_t th, int priority)
574 if (DosSetPriority(PRTYS_THREAD,
582 /*** Thread cancellation ***/
584 /* APC procedure for thread cancellation */
585 static void vlc_cancel_self (PVOID self)
587 struct vlc_thread *th = self;
589 if (likely(th != NULL))
593 void vlc_cancel (vlc_thread_t thread_id)
595 DosPostEventSem( thread_id->cancel_event );
596 so_cancel( thread_id->cancel_sock );
599 int vlc_savecancel (void)
603 struct vlc_thread *th = vlc_threadvar_get (thread_key);
605 return false; /* Main thread - cannot be cancelled anyway */
607 state = th->killable;
608 th->killable = false;
612 void vlc_restorecancel (int state)
614 struct vlc_thread *th = vlc_threadvar_get (thread_key);
615 assert (state == false || state == true);
618 return; /* Main thread - cannot be cancelled anyway */
620 assert (!th->killable);
621 th->killable = state != 0;
624 void vlc_testcancel (void)
626 struct vlc_thread *th = vlc_threadvar_get (thread_key);
628 return; /* Main thread - cannot be cancelled anyway */
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 );
635 if (th->killable && th->killed)
637 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
640 DosPostEventSem( th->done_event );
641 th->data = NULL; /* TODO: special value? */
642 vlc_thread_cleanup (th);
647 void vlc_control_cancel (int cmd, ...)
649 /* NOTE: This function only modifies thread-specific data, so there is no
650 * need to lock anything. */
653 struct vlc_thread *th = vlc_threadvar_get (thread_key);
655 return; /* Main thread - cannot be cancelled anyway */
660 case VLC_CLEANUP_PUSH:
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;
670 case VLC_CLEANUP_POP:
672 th->cleaners = th->cleaners->next;
679 static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
680 struct timeval *timeout )
682 struct vlc_thread *th = vlc_threadvar_get( thread_key );
688 FD_SET( th->cancel_sock, rdset );
690 nfds = MAX( nfds, th->cancel_sock + 1 );
693 rc = select( nfds, rdset, wrset, exset, timeout );
701 int vlc_poll( struct pollfd *fds, unsigned nfds, int timeout )
703 fd_set rdset, wrset, exset;
705 struct timeval tv = { 0, 0 };
712 for( unsigned i = 0; i < nfds; i++ )
714 int fd = fds[ i ].fd;
718 if(( unsigned )fd >= FD_SETSIZE )
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 );
734 div_t d = div( timeout, 1000 );
736 tv.tv_usec = d.rem * 1000;
739 val = vlc_select( val + 1, &rdset, &wrset, &exset,
740 ( timeout >= 0 ) ? &tv : NULL );
744 for( unsigned i = 0; i < nfds; i++ )
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 );
755 #define Q2LL( q ) ( *( long long * )&( q ))
760 /* We don't need the real date, just the value of a high precision timer */
763 if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
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);
770 return (d.quot * 1000000) + ((d.rem * 1000000) / freq);
774 void mwait (mtime_t deadline)
779 while ((delay = (deadline - mdate())) > 0)
782 if (unlikely(delay > 0x7fffffff))
790 void msleep (mtime_t delay)
792 mwait (mdate () + delay);
803 void (*func) (void *);
807 static void vlc_timer_do (void *arg)
809 struct vlc_timer *timer = arg;
815 DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
816 DosResetEventSem (timer->hev, &count);
821 timer->func (timer->data);
824 DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
828 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
830 struct vlc_timer *timer = malloc (sizeof (*timer));
838 DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
839 timer->htimer = NULLHANDLE;
842 timer->tid = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
848 void vlc_timer_destroy (vlc_timer_t timer)
850 if (timer->htimer != NULLHANDLE)
851 DosStopTimer (timer->htimer);
854 DosPostEventSem (timer->hev);
855 DosWaitThread (&timer->tid, DCWW_WAIT);
856 DosCloseEventSem (timer->hev);
861 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
862 mtime_t value, mtime_t interval)
864 if (timer->htimer != NULLHANDLE)
866 DosStopTimer (timer->htimer);
867 timer->htimer = NULLHANDLE;
876 value = (value + 999) / 1000;
877 interval = (interval + 999) / 1000;
879 timer->interval = interval;
880 if (DosAsyncTimer (value, (HSEM)timer->hev, &timer->htimer))
884 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
891 unsigned vlc_GetCPUCount (void)
895 DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
896 &numprocs, sizeof(numprocs));