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 static vlc_threadvar_t thread_key;
56 vlc_cleanup_t *cleaners;
58 void *(*entry) (void *);
62 static void vlc_cancel_self (PVOID dummy);
64 static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout, BOOL fCancelable )
72 struct vlc_thread *th = vlc_threadvar_get( thread_key );
73 if( th == NULL || !fCancelable )
75 /* Main thread - cannot be cancelled anyway
76 * Alien thread - out of our control
78 if( hev != NULLHANDLE )
79 return DosWaitEventSem( hev, ulTimeout );
81 return DosSleep( ulTimeout );
85 if( hev != NULLHANDLE )
87 asr[ n ].hsemCur = ( HSEM )hev;
91 asr[ n ].hsemCur = ( HSEM )th->cancel_event;
92 asr[ n ].ulUser = 0xFFFF;
95 DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
96 rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
97 DosCloseMuxWaitSem( hmux );
101 if( ulUser == 0xFFFF )
103 vlc_cancel_self( th );
104 return ERROR_INTERRUPT;
110 static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
112 return vlc_DosWaitEventSemEx( hev, ulTimeout, TRUE );
115 static ULONG vlc_Sleep (ULONG ulTimeout)
117 ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout, TRUE );
119 return ( rc != ERROR_TIMEOUT ) ? rc : 0;
122 static vlc_mutex_t super_mutex;
123 static vlc_cond_t super_variable;
124 extern vlc_rwlock_t config_lock, msg_lock;
127 void _CRT_term(void);
129 unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
131 unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
137 case 0 : /* Initialization */
138 if(_CRT_init() == -1)
141 vlc_mutex_init (&super_mutex);
142 vlc_cond_init (&super_variable);
143 vlc_threadvar_create (&thread_key, NULL);
144 vlc_rwlock_init (&config_lock);
145 vlc_rwlock_init (&msg_lock);
150 case 1 : /* Termination */
151 vlc_rwlock_destroy (&msg_lock);
152 vlc_rwlock_destroy (&config_lock);
153 vlc_threadvar_delete (&thread_key);
154 vlc_cond_destroy (&super_variable);
155 vlc_mutex_destroy (&super_mutex);
162 return 0; /* Failed */
166 void vlc_mutex_init( vlc_mutex_t *p_mutex )
168 /* This creates a recursive mutex. This is OK as fast mutexes have
169 * no defined behavior in case of recursive locking. */
170 DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
171 p_mutex->dynamic = true;
174 void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
176 DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
177 p_mutex->dynamic = true;
181 void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
183 assert (p_mutex->dynamic);
184 DosCloseMutexSem( p_mutex->hmtx );
187 void vlc_mutex_lock (vlc_mutex_t *p_mutex)
189 if (!p_mutex->dynamic)
190 { /* static mutexes */
191 int canc = vlc_savecancel ();
192 assert (p_mutex != &super_mutex); /* this one cannot be static */
194 vlc_mutex_lock (&super_mutex);
195 while (p_mutex->locked)
197 p_mutex->contention++;
198 vlc_cond_wait (&super_variable, &super_mutex);
199 p_mutex->contention--;
201 p_mutex->locked = true;
202 vlc_mutex_unlock (&super_mutex);
203 vlc_restorecancel (canc);
207 DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
210 int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
212 if (!p_mutex->dynamic)
213 { /* static mutexes */
216 assert (p_mutex != &super_mutex); /* this one cannot be static */
217 vlc_mutex_lock (&super_mutex);
218 if (!p_mutex->locked)
220 p_mutex->locked = true;
223 vlc_mutex_unlock (&super_mutex);
227 return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
230 void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
232 if (!p_mutex->dynamic)
233 { /* static mutexes */
234 assert (p_mutex != &super_mutex); /* this one cannot be static */
236 vlc_mutex_lock (&super_mutex);
237 assert (p_mutex->locked);
238 p_mutex->locked = false;
239 if (p_mutex->contention)
240 vlc_cond_broadcast (&super_variable);
241 vlc_mutex_unlock (&super_mutex);
245 DosReleaseMutexSem( p_mutex->hmtx );
248 /*** Condition variables ***/
251 CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
255 static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
257 /* Create a manual-reset event (manual reset is needed for broadcast). */
258 if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
260 p_condvar->clock = clock;
263 void vlc_cond_init (vlc_cond_t *p_condvar)
265 vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
268 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
270 vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
273 void vlc_cond_destroy (vlc_cond_t *p_condvar)
275 DosCloseEventSem( p_condvar->hev );
278 void vlc_cond_signal (vlc_cond_t *p_condvar)
283 /* This is suboptimal but works. */
284 vlc_cond_broadcast (p_condvar);
287 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
292 /* Wake all threads up (as the event HANDLE has manual reset) */
293 DosPostEventSem( p_condvar->hev );
296 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
302 { /* FIXME FIXME FIXME */
311 vlc_mutex_unlock (p_mutex);
312 rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT );
313 vlc_mutex_lock (p_mutex);
314 } while( rc == ERROR_INTERRUPT );
316 DosResetEventSem( p_condvar->hev, &ulPost );
319 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
327 { /* FIXME FIXME FIXME */
337 switch (p_condvar->clock)
339 case CLOCK_REALTIME: /* FIXME? sub-second precision */
340 total = CLOCK_FREQ * time (NULL);
343 assert (p_condvar->clock == CLOCK_MONOTONIC);
347 total = (deadline - total) / 1000;
351 ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
353 vlc_mutex_unlock (p_mutex);
354 rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
355 vlc_mutex_lock (p_mutex);
356 } while( rc == ERROR_INTERRUPT );
358 DosResetEventSem( p_condvar->hev, &ulPost );
360 return rc ? ETIMEDOUT : 0;
363 /*** Thread-specific variables (TLS) ***/
367 void (*destroy) (void *);
368 struct vlc_threadvar *prev;
369 struct vlc_threadvar *next;
370 } *vlc_threadvar_last = NULL;
372 int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
376 struct vlc_threadvar *var = malloc (sizeof (*var));
377 if (unlikely(var == NULL))
380 rc = DosAllocThreadLocalMemory( 1, &var->id );
387 var->destroy = destr;
391 vlc_mutex_lock (&super_mutex);
392 var->prev = vlc_threadvar_last;
394 var->prev->next = var;
396 vlc_threadvar_last = var;
397 vlc_mutex_unlock (&super_mutex);
401 void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
403 struct vlc_threadvar *var = *p_tls;
405 vlc_mutex_lock (&super_mutex);
406 if (var->prev != NULL)
407 var->prev->next = var->next;
409 if (var->next != NULL)
410 var->next->prev = var->prev;
412 vlc_threadvar_last = var->prev;
414 vlc_mutex_unlock (&super_mutex);
416 DosFreeThreadLocalMemory( var->id );
420 int vlc_threadvar_set (vlc_threadvar_t key, void *value)
422 *key->id = ( ULONG )value;
426 void *vlc_threadvar_get (vlc_threadvar_t key)
428 return ( void * )*key->id;
433 void vlc_threads_setup (libvlc_int_t *p_libvlc)
438 static void vlc_thread_cleanup (struct vlc_thread *th)
443 /* TODO: use RW lock or something similar */
444 vlc_mutex_lock (&super_mutex);
445 for (key = vlc_threadvar_last; key != NULL; key = key->prev)
447 void *value = vlc_threadvar_get (key);
448 if (value != NULL && key->destroy != NULL)
450 vlc_mutex_unlock (&super_mutex);
451 vlc_threadvar_set (key, NULL);
452 key->destroy (value);
456 vlc_mutex_unlock (&super_mutex);
460 DosCloseEventSem (th->cancel_event);
461 DosCloseEventSem (th->done_event );
466 static void vlc_entry( void *p )
468 struct vlc_thread *th = p;
470 vlc_threadvar_set (thread_key, th);
472 th->data = th->entry (th->data);
473 DosPostEventSem( th->done_event );
474 vlc_thread_cleanup (th);
477 static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
478 void *(*entry) (void *), void *data, int priority)
480 struct vlc_thread *th = malloc (sizeof (*th));
481 if (unlikely(th == NULL))
485 th->detached = detached;
486 th->killable = false; /* not until vlc_entry() ! */
490 if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
492 if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
495 th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
496 if((int)th->tid == -1)
499 if (p_handle != NULL)
503 DosSetPriority(PRTYS_THREAD,
511 DosCloseEventSem (th->cancel_event);
512 DosCloseEventSem (th->done_event);
518 int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
519 void *data, int priority)
521 return vlc_clone_attr (p_handle, false, entry, data, priority);
524 void vlc_join (vlc_thread_t th, void **result)
531 rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
532 } while( rc == ERROR_INTERRUPT );
537 DosCloseEventSem( th->cancel_event );
538 DosCloseEventSem( th->done_event );
543 int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
544 void *data, int priority)
547 if (p_handle == NULL)
550 return vlc_clone_attr (p_handle, true, entry, data, priority);
553 int vlc_set_priority (vlc_thread_t th, int priority)
555 if (DosSetPriority(PRTYS_THREAD,
563 /*** Thread cancellation ***/
565 /* APC procedure for thread cancellation */
566 static void vlc_cancel_self (PVOID self)
568 struct vlc_thread *th = self;
570 if (likely(th != NULL))
574 void vlc_cancel (vlc_thread_t thread_id)
576 DosPostEventSem( thread_id->cancel_event );
579 int vlc_savecancel (void)
583 struct vlc_thread *th = vlc_threadvar_get (thread_key);
585 return false; /* Main thread - cannot be cancelled anyway */
587 state = th->killable;
588 th->killable = false;
592 void vlc_restorecancel (int state)
594 struct vlc_thread *th = vlc_threadvar_get (thread_key);
595 assert (state == false || state == true);
598 return; /* Main thread - cannot be cancelled anyway */
600 assert (!th->killable);
601 th->killable = state != 0;
604 void vlc_testcancel (void)
606 struct vlc_thread *th = vlc_threadvar_get (thread_key);
608 return; /* Main thread - cannot be cancelled anyway */
610 /* This check is needed for the case that vlc_cancel() is followed by
611 * vlc_testcancel() without any cancellation point */
612 if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
613 vlc_cancel_self( th );
615 if (th->killable && th->killed)
617 for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
620 DosPostEventSem( th->done_event );
621 th->data = NULL; /* TODO: special value? */
622 vlc_thread_cleanup (th);
627 void vlc_control_cancel (int cmd, ...)
629 /* NOTE: This function only modifies thread-specific data, so there is no
630 * need to lock anything. */
633 struct vlc_thread *th = vlc_threadvar_get (thread_key);
635 return; /* Main thread - cannot be cancelled anyway */
640 case VLC_CLEANUP_PUSH:
642 /* cleaner is a pointer to the caller stack, no need to allocate
643 * and copy anything. As a nice side effect, this cannot fail. */
644 vlc_cleanup_t *cleaner = va_arg (ap, vlc_cleanup_t *);
645 cleaner->next = th->cleaners;
646 th->cleaners = cleaner;
650 case VLC_CLEANUP_POP:
652 th->cleaners = th->cleaners->next;
659 #define Q2LL( q ) ( *( long long * )&( q ))
664 /* We don't need the real date, just the value of a high precision timer */
667 if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
670 /* Convert to from (1/freq) to microsecond resolution */
671 /* We need to split the division to avoid 63-bits overflow */
672 lldiv_t d = lldiv (Q2LL(counter), freq);
674 return (d.quot * 1000000) + ((d.rem * 1000000) / freq);
678 void mwait (mtime_t deadline)
683 while ((delay = (deadline - mdate())) > 0)
686 if (unlikely(delay > 0x7fffffff))
694 void msleep (mtime_t delay)
696 mwait (mdate () + delay);
707 void (*func) (void *);
711 static void vlc_timer_do (void *arg)
713 struct vlc_timer *timer = arg;
719 DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
720 DosResetEventSem (timer->hev, &count);
725 timer->func (timer->data);
728 DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
732 int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
734 struct vlc_timer *timer = malloc (sizeof (*timer));
742 DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
743 timer->htimer = NULLHANDLE;
746 timer->tid = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
752 void vlc_timer_destroy (vlc_timer_t timer)
754 if (timer->htimer != NULLHANDLE)
755 DosStopTimer (timer->htimer);
758 DosPostEventSem (timer->hev);
759 DosWaitThread (&timer->tid, DCWW_WAIT);
760 DosCloseEventSem (timer->hev);
765 void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
766 mtime_t value, mtime_t interval)
768 if (timer->htimer != NULLHANDLE)
770 DosStopTimer (timer->htimer);
771 timer->htimer = NULLHANDLE;
780 value = (value + 999) / 1000;
781 interval = (interval + 999) / 1000;
783 timer->interval = interval;
784 if (DosAsyncTimer (value, (HSEM)timer->hev, &timer->htimer))
788 unsigned vlc_timer_getoverrun (vlc_timer_t timer)
795 unsigned vlc_GetCPUCount (void)
799 DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
800 &numprocs, sizeof(numprocs));