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 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
48 #include <sys/select.h>
50 #include <sys/builtin.h>
54 static vlc_threadvar_t thread_key
;
66 vlc_cleanup_t
*cleaners
;
68 void *(*entry
) (void *);
72 static void vlc_cancel_self (PVOID dummy
);
74 static ULONG
vlc_DosWaitEventSemEx( HEV hev
, ULONG ulTimeout
)
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
);
96 if( hev
!= NULLHANDLE
)
98 asr
[ n
].hsemCur
= ( HSEM
)hev
;
102 asr
[ n
].hsemCur
= ( HSEM
)th
->cancel_event
;
103 asr
[ n
].ulUser
= 0xFFFF;
106 DosCreateMuxWaitSem( NULL
, &hmux
, n
, asr
, DCMW_WAIT_ANY
);
107 rc
= DosWaitMuxWaitSem( hmux
, ulTimeout
, &ulUser
);
108 DosCloseMuxWaitSem( hmux
);
112 if( ulUser
== 0xFFFF )
114 vlc_cancel_self( th
);
115 return ERROR_INTERRUPT
;
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
;
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
)
148 case 0 : /* Initialization */
149 if(_CRT_init() == -1)
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
);
159 case 1 : /* Termination */
160 vlc_rwlock_destroy (&config_lock
);
161 vlc_threadvar_delete (&thread_key
);
168 return 0; /* Failed */
171 void vlc_once(vlc_once_t
*once
, void (*cb
)(void))
175 /* load once->done */
176 __atomic_xchg( &done
, once
->done
);
178 /* not initialized ? */
181 vlc_mutex_lock( &once
->mutex
);
183 /* load once->done */
184 __atomic_xchg( &done
, once
->done
);
186 /* still not initialized ? */
191 /* set once->done to 1 */
192 __atomic_xchg( &once
->done
, 1 );
195 vlc_mutex_unlock( &once
->mutex
);
199 /*** Thread-specific variables (TLS) ***/
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 *))
212 struct vlc_threadvar
*var
= malloc (sizeof (*var
));
213 if (unlikely(var
== NULL
))
216 rc
= DosAllocThreadLocalMemory( 1, &var
->id
);
223 var
->destroy
= destr
;
227 vlc_mutex_lock (&super_mutex
);
228 var
->prev
= vlc_threadvar_last
;
230 var
->prev
->next
= var
;
232 vlc_threadvar_last
= var
;
233 vlc_mutex_unlock (&super_mutex
);
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
;
248 vlc_threadvar_last
= var
->prev
;
250 vlc_mutex_unlock (&super_mutex
);
252 DosFreeThreadLocalMemory( var
->id
);
256 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
258 *key
->id
= ( ULONG
)value
;
262 void *vlc_threadvar_get (vlc_threadvar_t key
)
264 return ( void * )*key
->id
;
269 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
274 static void vlc_thread_cleanup (struct vlc_thread
*th
)
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
);
292 vlc_mutex_unlock (&super_mutex
);
296 DosCloseEventSem (th
->cancel_event
);
297 DosCloseEventSem (th
->done_event
);
299 soclose (th
->cancel_sock
);
305 static void vlc_entry( void *p
)
307 struct vlc_thread
*th
= p
;
309 vlc_threadvar_set (thread_key
, th
);
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
))
324 th
->detached
= detached
;
325 th
->killable
= false; /* not until vlc_entry() ! */
329 if( DosCreateEventSem (NULL
, &th
->cancel_event
, 0, FALSE
))
331 if( DosCreateEventSem (NULL
, &th
->done_event
, 0, FALSE
))
334 th
->cancel_sock
= socket (AF_LOCAL
, SOCK_STREAM
, 0);
335 if( th
->cancel_sock
< 0 )
338 th
->tid
= _beginthread (vlc_entry
, NULL
, 1024 * 1024, th
);
339 if((int)th
->tid
== -1)
342 if (p_handle
!= NULL
)
346 DosSetPriority(PRTYS_THREAD
,
354 soclose (th
->cancel_sock
);
355 DosCloseEventSem (th
->cancel_event
);
356 DosCloseEventSem (th
->done_event
);
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
)
375 rc
= vlc_WaitForSingleObject( th
->done_event
, SEM_INDEFINITE_WAIT
);
376 } while( rc
== ERROR_INTERRUPT
);
381 DosCloseEventSem( th
->cancel_event
);
382 DosCloseEventSem( th
->done_event
);
384 soclose( th
->cancel_sock
);
389 int vlc_clone_detach (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
390 void *data
, int priority
)
393 if (p_handle
== NULL
)
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
,
409 unsigned long vlc_thread_self(void)
411 return vlc_thread_id();
414 unsigned long vlc_thread_id (void)
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
))
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)
440 struct vlc_thread
*th
= vlc_threadvar_get(thread_key
);
442 return false; /* Main thread - cannot be cancelled anyway */
444 state
= th
->killable
;
445 th
->killable
= false;
449 void vlc_restorecancel (int state
)
451 struct vlc_thread
*th
= vlc_threadvar_get(thread_key
);
452 assert (state
== false || state
== true);
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
);
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
)
477 DosPostEventSem( th
->done_event
);
478 th
->data
= NULL
; /* TODO: special value? */
479 vlc_thread_cleanup (th
);
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
);
491 return; /* Main thread - cannot be cancelled anyway */
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
;
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
);
515 FD_SET( th
->cancel_sock
, rdset
);
517 nfds
= MAX( nfds
, th
->cancel_sock
+ 1 );
520 rc
= select( nfds
, rdset
, wrset
, exset
, timeout
);
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
;
539 struct timeval tv
= { 0, 0 };
546 for( unsigned i
= 0; i
< nfds
; i
++ )
548 int fd
= fds
[ i
].fd
;
551 fds
[ i
].revents
= 0;
553 if( fstat( fd
, &stbuf
) == -1 ||
554 (errno
= 0, !S_ISSOCK( stbuf
.st_mode
)))
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
))
573 if(( unsigned )fd
>= FD_SETSIZE
)
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 ? */
593 struct timeval
*ptv
= NULL
;
597 div_t d
= div( timeout
, 1000 );
599 tv
.tv_usec
= d
.rem
* 1000;
604 if (vlc_select( val
+ 1, &rdset
, &wrset
, &exset
, ptv
) == -1)
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 )
627 #define Q2LL( q ) ( *( long long * )&( q ))
630 vlc_tick_t
vlc_tick_now (void)
632 /* We don't need the real date, just the value of a high precision timer */
635 if (DosTmrQueryTime(&counter
) || DosTmrQueryFreq(&freq
))
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
);
646 void vlc_tick_wait (vlc_tick_t deadline
)
651 while ((delay
= (deadline
- vlc_tick_now())) > 0)
654 if (unlikely(delay
> 0x7fffffff))
661 #undef vlc_tick_sleep
662 void vlc_tick_sleep (vlc_tick_t delay
)
664 vlc_tick_wait (vlc_tick_now () + delay
);
675 void (*func
) (void *);
679 static void vlc_timer_do (void *arg
)
681 struct vlc_timer
*timer
= arg
;
687 DosWaitEventSem (timer
->hev
, SEM_INDEFINITE_WAIT
);
688 DosResetEventSem (timer
->hev
, &count
);
693 timer
->func (timer
->data
);
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
));
710 DosCreateEventSem (NULL
, &timer
->hev
, DC_SEM_SHARED
, FALSE
);
711 timer
->htimer
= NULLHANDLE
;
714 timer
->tid
= _beginthread (vlc_timer_do
, NULL
, 1024 * 1024, timer
);
720 void vlc_timer_destroy (vlc_timer_t timer
)
722 if (timer
->htimer
!= NULLHANDLE
)
723 DosStopTimer (timer
->htimer
);
726 DosPostEventSem (timer
->hev
);
727 DosWaitThread (&timer
->tid
, DCWW_WAIT
);
728 DosCloseEventSem (timer
->hev
);
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
;
743 if (value
== VLC_TIMER_DISARM
)
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
))
756 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
763 unsigned vlc_GetCPUCount (void)
767 DosQuerySysInfo(QSV_NUMPROCESSORS
, QSV_NUMPROCESSORS
,
768 &numprocs
, sizeof(numprocs
));