/*****************************************************************************
* thread.c : OS/2 back-end for LibVLC
*****************************************************************************
* Copyright (C) 1999-2011 VLC authors and VideoLAN
*
* Authors: KO Myung-Hun <komh@chollian.net>
* Jean-Marc Dressler <polux@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
* Gildas Bazin <gbazin@netcourrier.com>
* Clément Sténac
* Rémi Denis-Courmont
* Pierre Ynard
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_threads.h>
#include "../libvlc.h"
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <stdalign.h>
#include <stdatomic.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <sys/time.h>
#include <sys/select.h>
#include <sys/builtin.h>
#include <sys/stat.h>
#include <vlc_atomic.h>
/* Static mutex and condition variable */
static vlc_mutex_t super_mutex = VLC_STATIC_MUTEX;
/* Threads */
static thread_local struct vlc_thread *current_thread_ctx = NULL;
struct vlc_thread
{
TID tid;
HEV cancel_event;
HEV done_event;
int cancel_sock;
bool killable;
atomic_bool killed;
vlc_cleanup_t *cleaners;
void *(*entry) (void *);
void *data;
};
static void vlc_cancel_self (PVOID dummy);
static ULONG vlc_DosWaitEventSemEx( HEV hev, ULONG ulTimeout )
{
HMUX hmux;
SEMRECORD asr[ 2 ];
ULONG ulUser;
int n;
ULONG rc;
struct vlc_thread *th = current_thread_ctx;
if( th == NULL || !th->killable )
{
/* Main thread - cannot be cancelled anyway
* Alien thread - out of our control
* Cancel disabled thread - ignore cancel
*/
if( hev != NULLHANDLE )
return DosWaitEventSem( hev, ulTimeout );
return DosSleep( ulTimeout );
}
n = 0;
if( hev != NULLHANDLE )
{
asr[ n ].hsemCur = ( HSEM )hev;
asr[ n ].ulUser = 0;
n++;
}
asr[ n ].hsemCur = ( HSEM )th->cancel_event;
asr[ n ].ulUser = 0xFFFF;
n++;
DosCreateMuxWaitSem( NULL, &hmux, n, asr, DCMW_WAIT_ANY );
rc = DosWaitMuxWaitSem( hmux, ulTimeout, &ulUser );
DosCloseMuxWaitSem( hmux );
if( rc )
return rc;
if( ulUser == 0xFFFF )
{
vlc_cancel_self( th );
return ERROR_INTERRUPT;
}
return NO_ERROR;
}
static ULONG vlc_WaitForSingleObject (HEV hev, ULONG ulTimeout)
{
return vlc_DosWaitEventSemEx( hev, ulTimeout );
}
static ULONG vlc_Sleep (ULONG ulTimeout)
{
ULONG rc = vlc_DosWaitEventSemEx( NULLHANDLE, ulTimeout );
return ( rc != ERROR_TIMEOUT ) ? rc : 0;
}
/*** Thread-specific variables (TLS) ***/
struct vlc_threadvar
{
PULONG id;
void (*destroy) (void *);
struct vlc_threadvar *prev;
struct vlc_threadvar *next;
} *vlc_threadvar_last = NULL;
int vlc_threadvar_create (vlc_threadvar_t *p_tls, void (*destr) (void *))
{
ULONG rc;
struct vlc_threadvar *var = malloc (sizeof (*var));
if (unlikely(var == NULL))
return errno;
rc = DosAllocThreadLocalMemory( 1, &var->id );
if( rc )
{
free (var);
return EAGAIN;
}
var->destroy = destr;
var->next = NULL;
*p_tls = var;
if (destr != NULL)
{
vlc_mutex_lock(&super_mutex);
var->prev = vlc_threadvar_last;
if (var->prev != NULL)
var->prev->next = var;
vlc_threadvar_last = var;
vlc_mutex_unlock(&super_mutex);
}
return 0;
}
void vlc_threadvar_delete (vlc_threadvar_t *p_tls)
{
struct vlc_threadvar *var = *p_tls;
if (var->destroy != NULL)
{
vlc_mutex_lock(&super_mutex);
if (var->prev != NULL)
var->prev->next = var->next;
if (var->next != NULL)
var->next->prev = var->prev;
else
vlc_threadvar_last = var->prev;
vlc_mutex_unlock(&super_mutex);
}
DosFreeThreadLocalMemory( var->id );
free (var);
}
int vlc_threadvar_set (vlc_threadvar_t key, void *value)
{
*key->id = ( ULONG )value;
return 0;
}
void *vlc_threadvar_get (vlc_threadvar_t key)
{
return ( void * )*key->id;
}
static struct wait_bucket
{
HMTX lock;
HEV wait;
unsigned waiters;
} wait_buckets[ 32 ];
static void wait_bucket_init( void )
{
for( size_t i = 0; i < ARRAY_SIZE( wait_buckets ); i++ )
{
struct wait_bucket *bucket = wait_buckets + i;
DosCreateMutexSem( NULL, &bucket->lock, 0L, FALSE );
DosCreateEventSem( NULL, &bucket->wait, 0L, FALSE );
}
}
static void wait_bucket_destroy( void )
{
for( size_t i = 0; i < ARRAY_SIZE( wait_buckets ); i++ )
{
struct wait_bucket *bucket = wait_buckets + i;
DosCloseMutexSem( bucket->lock );
DosCloseEventSem( bucket->wait );
}
}
static struct wait_bucket *wait_bucket_get( atomic_uint *addr )
{
uintptr_t u = ( uintptr_t )addr;
size_t idx = ( u / alignof ( *addr )) % ARRAY_SIZE( wait_buckets );
return &wait_buckets[ idx ];
}
static struct wait_bucket *wait_bucket_enter( atomic_uint *addr )
{
struct wait_bucket *bucket = wait_bucket_get(addr);
DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );
bucket->waiters++;
return bucket;
}
static void wait_bucket_leave( void *data )
{
struct wait_bucket *bucket = data;
bucket->waiters--;
DosReleaseMutexSem( bucket->lock );
}
void vlc_atomic_wait( void *addr, unsigned value )
{
atomic_uint *futex = addr;
struct wait_bucket *bucket = wait_bucket_enter( futex );
vlc_cleanup_push( wait_bucket_leave, bucket );
if( value == atomic_load_explicit( futex, memory_order_relaxed ))
{
ULONG count;
DosReleaseMutexSem( bucket->lock );
vlc_WaitForSingleObject( bucket->wait, SEM_INDEFINITE_WAIT );
DosResetEventSem( bucket->wait, &count );
DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );
}
else
vlc_testcancel();
wait_bucket_leave( bucket );
vlc_cleanup_pop();
}
int vlc_atomic_timedwait(void *addr, unsigned value, vlc_tick_t deadline)
{
atomic_uint *futex = addr;
struct wait_bucket *bucket = wait_bucket_enter( futex );
ULONG rc = 0;
vlc_cleanup_push( wait_bucket_leave, bucket );
if( value == atomic_load_explicit( futex, memory_order_relaxed ))
{
vlc_tick_t delay;
DosReleaseMutexSem( bucket->lock );
do
{
ULONG ms;
ULONG count;
delay = deadline - vlc_tick_now();
if( delay < 0 )
ms = 0;
else if( delay >= VLC_TICK_FROM_MS( LONG_MAX ))
ms = LONG_MAX;
else
ms = MS_FROM_VLC_TICK( delay );
rc = vlc_WaitForSingleObject( bucket->wait, ms );
if( rc == 0 )
{
DosResetEventSem( bucket->wait, &count );
break;
}
} while( delay > 0 );
DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );
}
else
vlc_testcancel();
wait_bucket_leave( bucket );
vlc_cleanup_pop();
return rc == 0 ? 0 : ETIMEDOUT;
}
void vlc_atomic_notify_one(void *addr)
{
vlc_atomic_notify_all(addr);
}
void vlc_atomic_notify_all(void *addr)
{
struct wait_bucket *bucket = wait_bucket_get(addr);
DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );
if( bucket->waiters > 0 )
DosPostEventSem( bucket->wait );
DosReleaseMutexSem( bucket->lock);
}
/*** Threads ***/
void vlc_threads_setup (libvlc_int_t *p_libvlc)
{
(void) p_libvlc;
}
static void vlc_thread_cleanup (struct vlc_thread *th)
{
vlc_threadvar_t key;
retry:
/* TODO: use RW lock or something similar */
vlc_mutex_lock (&super_mutex);
for (key = vlc_threadvar_last; key != NULL; key = key->prev)
{
void *value = vlc_threadvar_get (key);
if (value != NULL)
{
vlc_mutex_unlock (&super_mutex);
vlc_threadvar_set (key, NULL);
assert(key->destroy != NULL);
key->destroy (value);
goto retry;
}
}
vlc_mutex_unlock (&super_mutex);
}
static void vlc_entry( void *p )
{
struct vlc_thread *th = p;
current_thread_ctx = th;
th->killable = true;
th->data = th->entry (th->data);
assert(th->data != VLC_THREAD_CANCELED); // don't hijack our internal values
DosPostEventSem( th->done_event );
vlc_thread_cleanup (th);
}
int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
void *data)
{
struct vlc_thread *th = malloc (sizeof (*th));
if (unlikely(th == NULL))
return ENOMEM;
th->entry = entry;
th->data = data;
th->killable = false; /* not until vlc_entry() ! */
atomic_init (&th->killed, false);
th->cleaners = NULL;
if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
goto error;
if( DosCreateEventSem (NULL, &th->done_event, 0, FALSE))
goto error;
th->cancel_sock = socket (AF_LOCAL, SOCK_STREAM, 0);
if( th->cancel_sock < 0 )
goto error;
th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
if((int)th->tid == -1)
goto error;
if (p_handle != NULL)
*p_handle = th;
return 0;
error:
soclose (th->cancel_sock);
DosCloseEventSem (th->cancel_event);
DosCloseEventSem (th->done_event);
free (th);
return ENOMEM;
}
void vlc_join (vlc_thread_t th, void **result)
{
ULONG rc;
do
{
vlc_testcancel();
rc = vlc_WaitForSingleObject( th->done_event, SEM_INDEFINITE_WAIT );
} while( rc == ERROR_INTERRUPT );
if (result != NULL)
*result = th->data;
DosCloseEventSem( th->cancel_event );
DosCloseEventSem( th->done_event );
soclose( th->cancel_sock );
free( th );
}
unsigned long vlc_thread_id (void)
{
return _gettid();
}
void (vlc_thread_set_name)(const char *name)
{
VLC_UNUSED(name);
}
/*** Thread cancellation ***/
/* APC procedure for thread cancellation */
static void vlc_cancel_self (PVOID self)
{
struct vlc_thread *th = self;
if (likely(th != NULL))
atomic_store_explicit (&th->killed, true, memory_order_relaxed);
}
void vlc_cancel (vlc_thread_t th)
{
atomic_store_explicit( &th->killed, true, memory_order_relaxed );
DosPostEventSem( th->cancel_event );
so_cancel( th->cancel_sock );
}
int vlc_savecancel (void)
{
int state;
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return false; /* Main thread - cannot be cancelled anyway */
state = th->killable;
th->killable = false;
return state;
}
void vlc_restorecancel (int state)
{
struct vlc_thread *th = current_thread_ctx;
assert (state == false || state == true);
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
assert (!th->killable);
th->killable = state != 0;
}
void vlc_testcancel (void)
{
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
if (!th->killable)
return;
/* This check is needed for the case that vlc_cancel() is followed by
* vlc_testcancel() without any cancellation point */
if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )
vlc_cancel_self( th );
if( !atomic_load_explicit( &th->killed, memory_order_relaxed ))
return;
th->killable = true; /* Do not re-enter cancellation cleanup */
for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
p->proc (p->data);
DosPostEventSem( th->done_event );
th->data = VLC_THREAD_CANCELED;
vlc_thread_cleanup (th);
_endthread();
}
void vlc_control_cancel (vlc_cleanup_t *cleaner)
{
/* NOTE: This function only modifies thread-specific data, so there is no
* need to lock anything. */
struct vlc_thread *th = current_thread_ctx;
if (th == NULL)
return; /* Main thread - cannot be cancelled anyway */
if (cleaner != NULL)
{
/* cleaner is a pointer to the caller stack, no need to allocate
* and copy anything. As a nice side effect, this cannot fail. */
cleaner->next = th->cleaners;
th->cleaners = cleaner;
}
else
{
th->cleaners = th->cleaners->next;
}
}
static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
struct timeval *timeout )
{
struct vlc_thread *th = current_thread_ctx;
int rc;
if( th )
{
FD_SET( th->cancel_sock, rdset );
nfds = MAX( nfds, th->cancel_sock + 1 );
}
rc = select( nfds, rdset, wrset, exset, timeout );
vlc_testcancel();
return rc;
}
/* Export vlc_poll_os2 directly regardless of EXPORTS of .def */
__declspec(dllexport)
int vlc_poll_os2( struct pollfd *fds, unsigned nfds, int timeout );
__declspec(dllexport)
int vlc_poll_os2( struct pollfd *fds, unsigned nfds, int timeout )
{
fd_set rdset, wrset, exset;
int non_sockets = 0;
struct timeval tv = { 0, 0 };
int val = -1;
FD_ZERO( &rdset );
FD_ZERO( &wrset );
FD_ZERO( &exset );
for( unsigned i = 0; i < nfds; i++ )
{
int fd = fds[ i ].fd;
struct stat stbuf;
fds[ i ].revents = 0;
if( fstat( fd, &stbuf ) == -1 ||
(errno = 0, !S_ISSOCK( stbuf.st_mode )))
{
if( fd >= 0 )
{
/* If regular files, assume readiness for requested modes */
fds[ i ].revents = ( !errno && S_ISREG( stbuf.st_mode ))
? ( fds[ i ].events &
( POLLIN | POLLOUT | POLLPRI ))
: POLLNVAL;
non_sockets++;
}
continue;
}
if( val < fd )
val = fd;
if(( unsigned )fd >= FD_SETSIZE )
{
errno = EINVAL;
return -1;
}
if( fds[ i ].events & POLLIN )
FD_SET( fd, &rdset );
if( fds[ i ].events & POLLOUT )
FD_SET( fd, &wrset );
if( fds[ i ].events & POLLPRI )
FD_SET( fd, &exset );
}
if( non_sockets > 0 )
timeout = 0; /* Just check pending sockets */
/* Sockets included ? */
if( val != -1)
{
struct timeval *ptv = NULL;
if( timeout >= 0 )
{
div_t d = div( timeout, 1000 );
tv.tv_sec = d.quot;
tv.tv_usec = d.rem * 1000;
ptv = &tv;
}
if (vlc_select( val + 1, &rdset, &wrset, &exset, ptv ) == -1)
return -1;
}
val = 0;
for( unsigned i = 0; i < nfds; i++ )
{
int fd = fds[ i ].fd;
if( fd >= 0 && fds[ i ].revents == 0 )
{
fds[ i ].revents = ( FD_ISSET( fd, &rdset ) ? POLLIN : 0 )
| ( FD_ISSET( fd, &wrset ) ? POLLOUT : 0 )
| ( FD_ISSET( fd, &exset ) ? POLLPRI : 0 );
}
if( fds[ i ].revents != 0 )
val++;
}
return val;
}
#define Q2LL( q ) ( *( long long * )&( q ))
/*** Clock ***/
vlc_tick_t vlc_tick_now (void)
{
/* We don't need the real date, just the value of a high precision timer */
QWORD counter;
ULONG freq;
if (DosTmrQueryTime(&counter) || DosTmrQueryFreq(&freq))
abort();
/* Convert to from (1/freq) to microsecond resolution */
/* We need to split the division to avoid 63-bits overflow */
lldiv_t d = lldiv (Q2LL(counter), freq);
return vlc_tick_from_sec( d.quot ) + vlc_tick_from_samples(d.rem, freq);
}
#undef vlc_tick_wait
void vlc_tick_wait (vlc_tick_t deadline)
{
vlc_tick_t delay;
vlc_testcancel();
while ((delay = (deadline - vlc_tick_now())) > 0)
{
delay /= 1000;
if (unlikely(delay > 0x7fffffff))
delay = 0x7fffffff;
vlc_Sleep (delay);
vlc_testcancel();
}
}
#undef vlc_tick_sleep
void vlc_tick_sleep (vlc_tick_t delay)
{
vlc_tick_wait (vlc_tick_now () + delay);
}
/*** Timers ***/
struct vlc_timer
{
TID tid;
HEV hev;
HTIMER htimer;
ULONG interval;
bool quit;
void (*func) (void *);
void *data;
};
static void vlc_timer_do (void *arg)
{
struct vlc_timer *timer = arg;
while (1)
{
ULONG count;
DosWaitEventSem (timer->hev, SEM_INDEFINITE_WAIT);
DosResetEventSem (timer->hev, &count);
if (timer->quit)
break;
timer->func (timer->data);
if (timer->interval)
DosAsyncTimer (timer->interval, (HSEM)timer->hev, &timer->htimer);
}
}
int vlc_timer_create (vlc_timer_t *id, void (*func) (void *), void *data)
{
struct vlc_timer *timer = malloc (sizeof (*timer));
if (timer == NULL)
return ENOMEM;
timer->func = func;
timer->data = data;
DosCreateEventSem (NULL, &timer->hev, DC_SEM_SHARED, FALSE);
timer->htimer = NULLHANDLE;
timer->interval = 0;
timer->quit = false;
timer->tid = _beginthread (vlc_timer_do, NULL, 1024 * 1024, timer);
*id = timer;
return 0;
}
void vlc_timer_destroy (vlc_timer_t timer)
{
if (timer->htimer != NULLHANDLE)
DosStopTimer (timer->htimer);
timer->quit = true;
DosPostEventSem (timer->hev);
DosWaitThread (&timer->tid, DCWW_WAIT);
DosCloseEventSem (timer->hev);
free (timer);
}
void vlc_timer_schedule (vlc_timer_t timer, bool absolute,
vlc_tick_t value, vlc_tick_t interval)
{
if (timer->htimer != NULLHANDLE)
{
DosStopTimer (timer->htimer);
timer->htimer = NULLHANDLE;
timer->interval = 0;
}
if (value == VLC_TIMER_DISARM)
return; /* Disarm */
if (absolute)
value -= vlc_tick_now ();
value = (value + 999) / 1000;
interval = (interval + 999) / 1000;
timer->interval = MS_FROM_VLC_TICK(interval);
if (DosAsyncTimer (MS_FROM_VLC_TICK(value), (HSEM)timer->hev, &timer->htimer))
abort ();
}
unsigned vlc_timer_getoverrun (vlc_timer_t timer)
{
(void)timer;
return 0;
}
/*** CPU ***/
unsigned vlc_GetCPUCount (void)
{
ULONG numprocs = 1;
DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
&numprocs, sizeof(numprocs));
return numprocs;
}
int _CRT_init(void);
void _CRT_term(void);
unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);
unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)
{
VLC_UNUSED (hmod);
switch (flag)
{
case 0 : /* Initialization */
if(_CRT_init() == -1)
return 0;
wait_bucket_init();
return 1;
case 1 : /* Termination */
wait_bucket_destroy();
// all thread vars should be deleted
assert(vlc_threadvar_last == NULL);
_CRT_term();
return 1;
}
return 0; /* Failed */
}
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|