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 vlc_mutex_t super_mutex
;
123 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
;
393 vlc_threadvar_last
= var
;
394 vlc_mutex_unlock (&super_mutex
);
398 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
400 struct vlc_threadvar
*var
= *p_tls
;
402 vlc_mutex_lock (&super_mutex
);
403 if (var
->prev
!= NULL
)
404 var
->prev
->next
= var
->next
;
406 vlc_threadvar_last
= var
->next
;
407 if (var
->next
!= NULL
)
408 var
->next
->prev
= var
->prev
;
409 vlc_mutex_unlock (&super_mutex
);
411 DosFreeThreadLocalMemory( var
->id
);
415 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
417 *key
->id
= ( ULONG
)value
;
421 void *vlc_threadvar_get (vlc_threadvar_t key
)
423 return ( void * )*key
->id
;
428 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
433 static void vlc_thread_cleanup (struct vlc_thread
*th
)
438 /* TODO: use RW lock or something similar */
439 vlc_mutex_lock (&super_mutex
);
440 for (key
= vlc_threadvar_last
; key
!= NULL
; key
= key
->prev
)
442 void *value
= vlc_threadvar_get (key
);
443 if (value
!= NULL
&& key
->destroy
!= NULL
)
445 vlc_mutex_unlock (&super_mutex
);
446 vlc_threadvar_set (key
, NULL
);
447 key
->destroy (value
);
451 vlc_mutex_unlock (&super_mutex
);
455 DosCloseEventSem (th
->cancel_event
);
456 DosCloseEventSem (th
->done_event
);
461 static void vlc_entry( void *p
)
463 struct vlc_thread
*th
= p
;
465 vlc_threadvar_set (thread_key
, th
);
467 th
->data
= th
->entry (th
->data
);
468 DosPostEventSem( th
->done_event
);
469 vlc_thread_cleanup (th
);
472 static int vlc_clone_attr (vlc_thread_t
*p_handle
, bool detached
,
473 void *(*entry
) (void *), void *data
, int priority
)
475 struct vlc_thread
*th
= malloc (sizeof (*th
));
476 if (unlikely(th
== NULL
))
480 th
->detached
= detached
;
481 th
->killable
= false; /* not until vlc_entry() ! */
485 if( DosCreateEventSem (NULL
, &th
->cancel_event
, 0, FALSE
))
487 if( DosCreateEventSem (NULL
, &th
->done_event
, 0, FALSE
))
490 th
->tid
= _beginthread (vlc_entry
, NULL
, 1024 * 1024, th
);
491 if((int)th
->tid
== -1)
494 if (p_handle
!= NULL
)
498 DosSetPriority(PRTYS_THREAD
,
506 DosCloseEventSem (th
->cancel_event
);
507 DosCloseEventSem (th
->done_event
);
513 int vlc_clone (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
514 void *data
, int priority
)
516 return vlc_clone_attr (p_handle
, false, entry
, data
, priority
);
519 void vlc_join (vlc_thread_t th
, void **result
)
526 rc
= vlc_WaitForSingleObject( th
->done_event
, SEM_INDEFINITE_WAIT
);
527 } while( rc
== ERROR_INTERRUPT
);
532 DosCloseEventSem( th
->cancel_event
);
533 DosCloseEventSem( th
->done_event
);
538 int vlc_clone_detach (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
539 void *data
, int priority
)
542 if (p_handle
== NULL
)
545 return vlc_clone_attr (p_handle
, true, entry
, data
, priority
);
548 int vlc_set_priority (vlc_thread_t th
, int priority
)
550 if (DosSetPriority(PRTYS_THREAD
,
558 /*** Thread cancellation ***/
560 /* APC procedure for thread cancellation */
561 static void vlc_cancel_self (PVOID self
)
563 struct vlc_thread
*th
= self
;
565 if (likely(th
!= NULL
))
569 void vlc_cancel (vlc_thread_t thread_id
)
571 DosPostEventSem( thread_id
->cancel_event
);
574 int vlc_savecancel (void)
578 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
580 return false; /* Main thread - cannot be cancelled anyway */
582 state
= th
->killable
;
583 th
->killable
= false;
587 void vlc_restorecancel (int state
)
589 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
590 assert (state
== false || state
== true);
593 return; /* Main thread - cannot be cancelled anyway */
595 assert (!th
->killable
);
596 th
->killable
= state
!= 0;
599 void vlc_testcancel (void)
601 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
603 return; /* Main thread - cannot be cancelled anyway */
605 /* This check is needed for the case that vlc_cancel() is followed by
606 * vlc_testcancel() without any cancellation point */
607 if( DosWaitEventSem( th
->cancel_event
, 0 ) == NO_ERROR
)
608 vlc_cancel_self( th
);
610 if (th
->killable
&& th
->killed
)
612 for (vlc_cleanup_t
*p
= th
->cleaners
; p
!= NULL
; p
= p
->next
)
615 DosPostEventSem( th
->done_event
);
616 th
->data
= NULL
; /* TODO: special value? */
617 vlc_thread_cleanup (th
);
622 void vlc_control_cancel (int cmd
, ...)
624 /* NOTE: This function only modifies thread-specific data, so there is no
625 * need to lock anything. */
628 struct vlc_thread
*th
= vlc_threadvar_get (thread_key
);
630 return; /* Main thread - cannot be cancelled anyway */
635 case VLC_CLEANUP_PUSH
:
637 /* cleaner is a pointer to the caller stack, no need to allocate
638 * and copy anything. As a nice side effect, this cannot fail. */
639 vlc_cleanup_t
*cleaner
= va_arg (ap
, vlc_cleanup_t
*);
640 cleaner
->next
= th
->cleaners
;
641 th
->cleaners
= cleaner
;
645 case VLC_CLEANUP_POP
:
647 th
->cleaners
= th
->cleaners
->next
;
654 #define Q2LL( q ) ( *( long long * )&( q ))
659 /* We don't need the real date, just the value of a high precision timer */
662 if (DosTmrQueryTime(&counter
) || DosTmrQueryFreq(&freq
))
665 /* Convert to from (1/freq) to microsecond resolution */
666 /* We need to split the division to avoid 63-bits overflow */
667 lldiv_t d
= lldiv (Q2LL(counter
), freq
);
669 return (d
.quot
* 1000000) + ((d
.rem
* 1000000) / freq
);
673 void mwait (mtime_t deadline
)
678 while ((delay
= (deadline
- mdate())) > 0)
681 if (unlikely(delay
> 0x7fffffff))
689 void msleep (mtime_t delay
)
691 mwait (mdate () + delay
);
702 void (*func
) (void *);
706 static void vlc_timer_do (void *arg
)
708 struct vlc_timer
*timer
= arg
;
714 DosWaitEventSem (timer
->hev
, SEM_INDEFINITE_WAIT
);
715 DosResetEventSem (timer
->hev
, &count
);
720 timer
->func (timer
->data
);
723 DosAsyncTimer (timer
->interval
, (HSEM
)timer
->hev
, &timer
->htimer
);
727 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
729 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
737 DosCreateEventSem (NULL
, &timer
->hev
, DC_SEM_SHARED
, FALSE
);
738 timer
->htimer
= NULLHANDLE
;
741 timer
->tid
= _beginthread (vlc_timer_do
, NULL
, 1024 * 1024, timer
);
747 void vlc_timer_destroy (vlc_timer_t timer
)
749 if (timer
->htimer
!= NULLHANDLE
)
750 DosStopTimer (timer
->htimer
);
753 DosPostEventSem (timer
->hev
);
754 DosWaitThread (&timer
->tid
, DCWW_WAIT
);
755 DosCloseEventSem (timer
->hev
);
760 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
761 mtime_t value
, mtime_t interval
)
763 if (timer
->htimer
!= NULLHANDLE
)
765 DosStopTimer (timer
->htimer
);
766 timer
->htimer
= NULLHANDLE
;
775 value
= (value
+ 999) / 1000;
776 interval
= (interval
+ 999) / 1000;
778 timer
->interval
= interval
;
779 if (DosAsyncTimer (value
, (HSEM
)timer
->hev
, &timer
->htimer
))
783 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
790 unsigned vlc_GetCPUCount (void)
794 DosQuerySysInfo(QSV_NUMPROCESSORS
, QSV_NUMPROCESSORS
,
795 &numprocs
, sizeof(numprocs
));