[vlc-devel] [PATCH 19/41] Implement thread support for OS/2
Rémi Denis-Courmont
remi at remlab.net
Mon Oct 10 17:13:04 CEST 2011
Comments inline...
Le lundi 10 octobre 2011 14:43:58 KO Myung-Hun, vous avez écrit :
> ---
> include/vlc_common.h | 15 +
> include/vlc_threads.h | 63 ++++
> src/Makefile.am | 13 +
> src/os2/thread.c | 932
> +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1023
> insertions(+), 0 deletions(-)
> create mode 100644 src/os2/thread.c
>
> diff --git a/include/vlc_common.h b/include/vlc_common.h
> index 8e03ed3..95a9531 100644
> --- a/include/vlc_common.h
> +++ b/include/vlc_common.h
> @@ -134,6 +134,9 @@
>
> #define VLC_API VLC_EXTERN VLC_EXPORT
>
> +#ifdef __OS2__
> +# define pthread_sigmask sigprocmask
> +#endif
That feels a bit out of place.
Is there any reason why it's not in <vlc_threads.h> ?
>
> /*************************************************************************
> **** * Basic types definitions
> @@ -149,6 +152,11 @@
> #include <sys/syslimits.h>
> #endif
>
> +#ifdef __OS2__
> +# include <unistd.h>
> +# include <time.h>
> +#endif
> +
Those two headers should not be needed in <vlc_common.h>. Normally, files that
dependent on them already include them explicitly.
> /* Counter for statistics and profiling */
> typedef unsigned long count_t;
>
> @@ -476,6 +484,13 @@ typedef int ( * vlc_callback_t ) ( vlc_object_t *,
> /* variable's object */ # include <windows.h>
> #endif
>
> +#ifdef __OS2__
> +# define OS2EMX_PLAIN_CHAR
> +# define INCL_BASE
> +# define INCL_PM
> +# include <os2.h>
> +#endif
> +
> #include "vlc_mtime.h"
> #include "vlc_threads.h"
>
> diff --git a/include/vlc_threads.h b/include/vlc_threads.h
> index 3fefbaf..e76a6bc 100644
> --- a/include/vlc_threads.h
> +++ b/include/vlc_threads.h
> @@ -38,6 +38,10 @@
> #elif defined( WIN32 )
> # include <process.h> /* Win32
> API */
>
> +#elif defined( __OS2__ ) /* OS/2
> API */
> +# include <stdlib.h>
This should not be needed. Unfortunately, for historical reasons, stdlib.h is
included in <vlc_common.h> anyway.
> +# include <errno.h>
> +
> #else /* pthreads (like Linux &
> BSD) */
> # define LIBVLC_USE_PTHREAD 1
> # define LIBVLC_USE_PTHREAD_CANCEL 1
> @@ -90,6 +94,14 @@
> # define VLC_THREAD_PRIORITY_HIGHEST \
> THREAD_PRIORITY_TIME_CRITICAL
>
> +#elif defined(__OS2__)
> +# define VLC_THREAD_PRIORITY_LOW 0
> +# define VLC_THREAD_PRIORITY_INPUT MAKESHORT( PRTYD_MAXIMUM / 2,
> PRTYC_REGULAR )
> +# define VLC_THREAD_PRIORITY_AUDIO MAKESHORT(
> PRTYD_MAXIMUM, PRTYC_REGULAR )
> +# define VLC_THREAD_PRIORITY_VIDEO 0
> +# define VLC_THREAD_PRIORITY_OUTPUT MAKESHORT( PRTYD_MAXIMUM / 2,
> PRTYC_REGULAR )
> +# define VLC_THREAD_PRIORITY_HIGHEST MAKESHORT( 0,
> PRTYC_TIMECRITICAL )
> +
> #else
> # define VLC_THREAD_PRIORITY_LOW 0
> # define VLC_THREAD_PRIORITY_INPUT 0
> @@ -161,6 +173,57 @@ typedef struct
>
> typedef struct vlc_threadvar *vlc_threadvar_t;
> typedef struct vlc_timer *vlc_timer_t;
> +
> +#elif defined( __OS2__ )
> +typedef struct vlc_thread *vlc_thread_t;
> +
> +typedef struct
> +{
> + bool dynamic;
> + union
> + {
> + struct
> + {
> + bool locked;
> + unsigned long contention;
> + };
> + HMTX hmtx;
> + };
> +} vlc_mutex_t;
> +
> +#define VLC_STATIC_MUTEX { false, { { false, 0 } } }
> +
> +typedef struct
> +{
> + HEV hev;
> + unsigned clock;
> +} vlc_cond_t;
> +
> +#define VLC_STATIC_COND { 0, 0 }
> +
> +typedef struct
> +{
> + HEV hev;
> + HMTX wait_mutex;
> + HMTX count_mutex;
> + int count;
> + int max_count;
> +} vlc_sem_t;
A semaphore can be reimplemented with one mutex, one condition variable and
one integer. So it's surprising that 'HMTX' are required here.
> +
> +typedef struct
> +{
> + vlc_mutex_t mutex;
> + vlc_cond_t wait;
> + unsigned long readers;
> + unsigned long writers;
> + int writer;
> +} vlc_rwlock_t;
> +#define VLC_STATIC_RWLOCK \
> + { VLC_STATIC_MUTEX, VLC_STATIC_COND, 0, 0, 0 }
> +
> +typedef struct vlc_threadvar *vlc_threadvar_t;
> +typedef struct vlc_timer *vlc_timer_t;
> +
> #endif
>
> #if defined( WIN32 ) && !defined ETIMEDOUT
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 3210814..9cfa200 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -197,6 +197,7 @@ EXTRA_libvlccore_la_SOURCES = \
> $(SOURCES_libvlc_darwin) \
> $(SOURCES_libvlc_linux) \
> $(SOURCES_libvlc_win32) \
> + $(SOURCES_libvlc_os2) \
> $(SOURCES_libvlc_other) \
> $(SOURCES_libvlc_httpd) \
> $(SOURCES_libvlc_sout) \
> @@ -217,12 +218,16 @@ else
> if HAVE_SYMBIAN
> #libvlccore_la_SOURCES += $(SOURCES_libvlc_symbian)
> else
> +if HAVE_OS2
> +libvlccore_la_SOURCES += $(SOURCES_libvlc_os2)
> +else
> libvlccore_la_SOURCES += $(SOURCES_libvlc_other)
> endif
> endif
> endif
> endif
> endif
> +endif
> if BUILD_HTTPD
> libvlccore_la_SOURCES += $(SOURCES_libvlc_httpd)
> endif
> @@ -271,6 +276,14 @@ SOURCES_libvlc_symbian = \
> win32/plugin.c \
> $(NULL)
>
> +SOURCES_libvlc_os2 = \
> + posix/dirs.c \
> + misc/atomic.c \
> + posix/filesystem.c \
> + os2/thread.c \
> + posix/specific.c \
> + $(NULL)
> +
> SOURCES_libvlc_other = \
> posix/dirs.c \
> misc/atomic.c \
> diff --git a/src/os2/thread.c b/src/os2/thread.c
> new file mode 100644
> index 0000000..131e6b6
> --- /dev/null
> +++ b/src/os2/thread.c
> @@ -0,0 +1,932 @@
> +/*************************************************************************
> **** + * thread.c : OS/2 back-end for LibVLC
> +
> **************************************************************************
> ***
> + * Copyright (C) 2010 KO Myung-Hun
This being based on src/win32/thread.c should probably carry the original
copyright notice as well, but JB would know what it should be.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU 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 "libvlc.h"
> +#include <stdarg.h>
> +#include <assert.h>
> +#include <limits.h>
> +#include <errno.h>
> +
> +static vlc_threadvar_t thread_key;
> +
> +/**
> + * Per-thread data
> + */
> +struct vlc_thread
> +{
> + TID tid;
> + HEV cancel_event;
> + HEV done_event;
> +
> + bool detached;
> + bool killable;
> + 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, BOOL
> fCancelable ) +{
> + HMUX hmux;
> + SEMRECORD asr[ 2 ];
> + ULONG ulUser;
> + ULONG rc;
> +
> + struct vlc_thread *th = vlc_threadvar_get( thread_key );
> + if( th == NULL || !fCancelable )
> + {
> + /* Main thread - cannot be cancelled anyway */
> + return DosWaitEventSem( hev, ulTimeout );
> + }
> +
> + asr[ 0 ].hsemCur = ( HSEM )hev;
> + asr[ 0 ].ulUser = 0;
> + asr[ 1 ].hsemCur = ( HSEM )th->cancel_event;
> + asr[ 1 ].ulUser = 0xFFFF;
> + DosCreateMuxWaitSem( NULL, &hmux, 2, 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, TRUE );
> +}
> +
> +static ULONG vlc_Sleep (ULONG ulTimeout)
> +{
> + DosSleep( ulTimeout );
> +
> + return 0;
> +}
I guess this will cause very long delays if ulTimeout is large, and the thread
got canceled.
> +
> +vlc_mutex_t super_mutex;
> +vlc_cond_t super_variable;
> +extern vlc_rwlock_t config_lock, msg_lock;
> +
> +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;
> +
> + vlc_mutex_init (&super_mutex);
> + vlc_cond_init (&super_variable);
> + vlc_threadvar_create (&thread_key, NULL);
> + vlc_rwlock_init (&config_lock);
> + vlc_rwlock_init (&msg_lock);
> + vlc_CPU_init ();
> +
> + return 1;
> +
> + case 1 : /* Termination */
> + vlc_rwlock_destroy (&msg_lock);
> + vlc_rwlock_destroy (&config_lock);
> + vlc_threadvar_delete (&thread_key);
> + vlc_cond_destroy (&super_variable);
> + vlc_mutex_destroy (&super_mutex);
> +
> + _CRT_term();
> +
> + return 1;
> + }
> +
> + return 0; /* Failed */
> +}
> +
> +/*** Mutexes ***/
> +void vlc_mutex_init( vlc_mutex_t *p_mutex )
> +{
> + /* This creates a recursive mutex. This is OK as fast mutexes have
> + * no defined behavior in case of recursive locking. */
> + DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
> + p_mutex->dynamic = true;
> +}
> +
> +void vlc_mutex_init_recursive( vlc_mutex_t *p_mutex )
> +{
> + DosCreateMutexSem( NULL, &p_mutex->hmtx, 0, FALSE );
> + p_mutex->dynamic = true;
> +}
> +
> +
> +void vlc_mutex_destroy (vlc_mutex_t *p_mutex)
> +{
> + assert (p_mutex->dynamic);
> + DosCloseMutexSem( p_mutex->hmtx );
> +}
> +
> +void vlc_mutex_lock (vlc_mutex_t *p_mutex)
> +{
> + if (!p_mutex->dynamic)
> + { /* static mutexes */
> + int canc = vlc_savecancel ();
> + assert (p_mutex != &super_mutex); /* this one cannot be static */
> +
> + vlc_mutex_lock (&super_mutex);
> + while (p_mutex->locked)
> + {
> + p_mutex->contention++;
> + vlc_cond_wait (&super_variable, &super_mutex);
> + p_mutex->contention--;
> + }
> + p_mutex->locked = true;
> + vlc_mutex_unlock (&super_mutex);
> + vlc_restorecancel (canc);
> + return;
> + }
> +
> + DosRequestMutexSem(p_mutex->hmtx, SEM_INDEFINITE_WAIT);
> +}
> +
> +int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
> +{
> + if (!p_mutex->dynamic)
> + { /* static mutexes */
> + int ret = EBUSY;
> +
> + assert (p_mutex != &super_mutex); /* this one cannot be static */
> + vlc_mutex_lock (&super_mutex);
> + if (!p_mutex->locked)
> + {
> + p_mutex->locked = true;
> + ret = 0;
> + }
> + vlc_mutex_unlock (&super_mutex);
> + return ret;
> + }
> +
> + return DosRequestMutexSem( p_mutex->hmtx, 0 ) ? EBUSY : 0;
> +}
> +
> +void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
> +{
> + if (!p_mutex->dynamic)
> + { /* static mutexes */
> + assert (p_mutex != &super_mutex); /* this one cannot be static */
> +
> + vlc_mutex_lock (&super_mutex);
> + assert (p_mutex->locked);
> + p_mutex->locked = false;
> + if (p_mutex->contention)
> + vlc_cond_broadcast (&super_variable);
> + vlc_mutex_unlock (&super_mutex);
> + return;
> + }
> +
> + DosReleaseMutexSem( p_mutex->hmtx );
> +}
> +
> +/*** Condition variables ***/
> +enum
> +{
> + CLOCK_REALTIME=0, /* must be zero for VLC_STATIC_COND */
> + CLOCK_MONOTONIC,
> +};
> +
> +static void vlc_cond_init_common (vlc_cond_t *p_condvar, unsigned clock)
> +{
> + /* Create a manual-reset event (manual reset is needed for broadcast).
> */ + if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE))
> + abort();
> + p_condvar->clock = clock;
> +}
> +
> +void vlc_cond_init (vlc_cond_t *p_condvar)
> +{
> + vlc_cond_init_common (p_condvar, CLOCK_MONOTONIC);
> +}
> +
> +void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
> +{
> + vlc_cond_init_common (p_condvar, CLOCK_REALTIME);
> +}
> +
> +void vlc_cond_destroy (vlc_cond_t *p_condvar)
> +{
> + DosCloseEventSem( p_condvar->hev );
> +}
> +
> +void vlc_cond_signal (vlc_cond_t *p_condvar)
> +{
> + if (!p_condvar->hev)
> + return;
> +
> + /* This is suboptimal but works. */
> + vlc_cond_broadcast (p_condvar);
> +}
> +
> +void vlc_cond_broadcast (vlc_cond_t *p_condvar)
> +{
> + if (!p_condvar->hev)
> + return;
> +
> + /* Wake all threads up (as the event HANDLE has manual reset) */
> + DosPostEventSem( p_condvar->hev );
> +}
> +
> +void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
> +{
> + ULONG ulPost;
> + ULONG rc;
> +
> + if (!p_condvar->hev)
> + { /* FIXME FIXME FIXME */
> + msleep (50000);
> + return;
> + }
> +
> + do
> + {
> + vlc_testcancel();
> +
> + vlc_mutex_unlock (p_mutex);
> + rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT
> ); + vlc_mutex_lock (p_mutex);
> + } while( rc == ERROR_INTERRUPT );
> +
> + DosResetEventSem( p_condvar->hev, &ulPost );
> +}
> +
> +int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
> + mtime_t deadline)
> +{
> + ULONG ulTimeout;
> + ULONG ulPost;
> + ULONG rc;
> +
> + if (!p_condvar->hev)
> + { /* FIXME FIXME FIXME */
> + msleep (50000);
> + return;
> + }
> +
> + do
> + {
> + vlc_testcancel();
> +
> + mtime_t total;
> + switch (p_condvar->clock)
> + {
> + case CLOCK_REALTIME: /* FIXME? sub-second precision */
> + total = CLOCK_FREQ * time (NULL);
> + break;
> + default:
> + assert (p_condvar->clock == CLOCK_MONOTONIC);
> + total = mdate();
> + break;
> + }
> + total = (deadline - total) / 1000;
> + if( total < 0 )
> + total = 0;
> +
> + ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
> +
> + vlc_mutex_unlock (p_mutex);
> + rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
> + vlc_mutex_lock (p_mutex);
> + } while( rc == ERROR_INTERRUPT );
> +
> + DosResetEventSem( p_condvar->hev, &ulPost );
> +
> + return rc ? ETIMEDOUT : 0;
> +}
> +
> +/*** Semaphore ***/
> +void vlc_sem_init (vlc_sem_t *sem, unsigned value)
> +{
> + if (DosCreateEventSem(NULL, &sem->hev, 0, value > 0 ? TRUE : FALSE))
> + abort ();
> +
> + if (DosCreateMutexSem(NULL, &sem->wait_mutex, 0, FALSE))
> + abort ();
> +
> + if (DosCreateMutexSem(NULL, &sem->count_mutex, 0, FALSE))
> + abort ();
> +
> + sem->count = value;
> + sem->max_count = 0x7FFFFFFF;
It seems that 'max_count' is constant, so you probably don't need to store it.
> +}
> +
> +void vlc_sem_destroy (vlc_sem_t *sem)
> +{
> + DosCloseEventSem (sem->hev);
> + DosCloseMutexSem (sem->wait_mutex);
> + DosCloseMutexSem (sem->count_mutex);
> +}
> +
> +int vlc_sem_post (vlc_sem_t *sem)
> +{
> + DosRequestMutexSem(sem->count_mutex, SEM_INDEFINITE_WAIT);
> +
> + if (sem->count < sem->max_count)
> + {
> + sem->count++;
> + DosPostEventSem(sem->hev);
> + }
> +
> + DosReleaseMutexSem(sem->count_mutex);
> +
> + return 0; /* FIXME */
> +}
> +
> +void vlc_sem_wait (vlc_sem_t *sem)
> +{
> + ULONG rc;
> +
> + do
> + {
> + vlc_testcancel ();
> +
> + DosRequestMutexSem(sem->wait_mutex, SEM_INDEFINITE_WAIT);
> +
> + rc = vlc_WaitForSingleObject (sem->hev, SEM_INDEFINITE_WAIT );
> +
> + if (!rc)
> + {
> + DosRequestMutexSem(sem->count_mutex, SEM_INDEFINITE_WAIT);
> +
> + sem->count--;
> + if (sem->count == 0)
> + {
> + ULONG ulPost;
> +
> + DosResetEventSem(sem->hev, &ulPost);
> + }
> +
> + DosReleaseMutexSem(sem->count_mutex);
> + }
> +
> + DosReleaseMutexSem(sem->wait_mutex);
> + } while (rc == ERROR_INTERRUPT);
> +}
> +
> +/*** Read/write locks */
> +void vlc_rwlock_init (vlc_rwlock_t *lock)
> +{
> + vlc_mutex_init (&lock->mutex);
> + vlc_cond_init (&lock->wait);
> + lock->readers = 0; /* active readers */
> + lock->writers = 0; /* waiting or active writers */
> + lock->writer = 0; /* ID of active writer */
> +}
> +
> +void vlc_rwlock_destroy (vlc_rwlock_t *lock)
> +{
> + vlc_cond_destroy (&lock->wait);
> + vlc_mutex_destroy (&lock->mutex);
> +}
> +
> +void vlc_rwlock_rdlock (vlc_rwlock_t *lock)
> +{
> + vlc_mutex_lock (&lock->mutex);
> + /* Recursive read-locking is allowed. With the infos available:
> + * - the loosest possible condition (no active writer) is:
> + * (lock->writer != 0)
> + * - the strictest possible condition is:
> + * (lock->writer != 0 || (lock->readers == 0 && lock->writers >
> 0)) + * or (lock->readers == 0 && (lock->writer != 0 || lock->writers
> > 0)) + */
> + while (lock->writer != 0)
> + {
> + assert (lock->readers == 0);
> + vlc_cond_wait (&lock->wait, &lock->mutex);
> + }
> + if (unlikely(lock->readers == ULONG_MAX))
> + abort ();
> + lock->readers++;
> + vlc_mutex_unlock (&lock->mutex);
> +}
> +
> +static void vlc_rwlock_rdunlock (vlc_rwlock_t *lock)
> +{
> + vlc_mutex_lock (&lock->mutex);
> + assert (lock->readers > 0);
> +
> + /* If there are no readers left, wake up a writer. */
> + if (--lock->readers == 0 && lock->writers > 0)
> + vlc_cond_signal (&lock->wait);
> + vlc_mutex_unlock (&lock->mutex);
> +}
> +
> +void vlc_rwlock_wrlock (vlc_rwlock_t *lock)
> +{
> + vlc_mutex_lock (&lock->mutex);
> + if (unlikely(lock->writers == ULONG_MAX))
> + abort ();
> + lock->writers++;
> + /* Wait until nobody owns the lock in either way. */
> + while ((lock->readers > 0) || (lock->writer != 0))
> + vlc_cond_wait (&lock->wait, &lock->mutex);
> + lock->writers--;
> + assert (lock->writer == 0);
> + lock->writer = _gettid ();
> + vlc_mutex_unlock (&lock->mutex);
> +}
> +
> +static void vlc_rwlock_wrunlock (vlc_rwlock_t *lock)
> +{
> + vlc_mutex_lock (&lock->mutex);
> + assert (lock->writer == _gettid ());
> + assert (lock->readers == 0);
> + lock->writer = 0; /* Write unlock */
> +
> + /* Let reader and writer compete. Scheduler decides who wins. */
> + vlc_cond_broadcast (&lock->wait);
> + vlc_mutex_unlock (&lock->mutex);
> +}
> +
> +void vlc_rwlock_unlock (vlc_rwlock_t *lock)
> +{
> + /* Note: If the lock is held for reading, lock->writer is nul.
> + * If the lock is held for writing, only this thread can store a value
> to + * lock->writer. Either way, lock->writer is safe to fetch here.
> */ + if (lock->writer != 0)
> + vlc_rwlock_wrunlock (lock);
> + else
> + vlc_rwlock_rdunlock (lock);
> +}
> +
> +/*** 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 ENOMEM;
> + }
> +
> + var->destroy = destr;
> + var->next = NULL;
> + *p_tls = var;
> +
> + vlc_mutex_lock (&super_mutex);
> + var->prev = vlc_threadvar_last;
> + 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;
> +
> + vlc_mutex_lock (&super_mutex);
> + if (var->prev != NULL)
> + var->prev->next = var->next;
> + else
> + vlc_threadvar_last = var->next;
> + if (var->next != NULL)
> + var->next->prev = 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;
> +}
> +
> +
> +/*** 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 && key->destroy != NULL)
> + {
> + vlc_mutex_unlock (&super_mutex);
> + vlc_threadvar_set (key, NULL);
> + key->destroy (value);
> + goto retry;
> + }
> + }
> + vlc_mutex_unlock (&super_mutex);
> +
> + if (th->detached)
> + {
> + DosCloseEventSem (th->cancel_event);
> + DosCloseEventSem (th->done_event );
> + free (th);
> + }
> +}
> +
> +static void vlc_entry( void *p )
> +{
> + struct vlc_thread *th = p;
> +
> + vlc_threadvar_set (thread_key, th);
> + th->killable = true;
> + th->data = th->entry (th->data);
> + DosPostEventSem( th->done_event );
> + vlc_thread_cleanup (th);
> +}
> +
> +static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
> + void *(*entry) (void *), void *data, int
> priority) +{
> + struct vlc_thread *th = malloc (sizeof (*th));
> + if (unlikely(th == NULL))
> + return ENOMEM;
> + th->entry = entry;
> + th->data = data;
> + th->detached = detached;
> + th->killable = false; /* not until vlc_entry() ! */
> + 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->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
> + if((int)th->tid == -1)
> + goto error;
> +
> + if (p_handle != NULL)
> + *p_handle = th;
> +
> + if (priority)
> + DosSetPriority(PRTYS_THREAD,
> + HIBYTE(priority),
> + LOBYTE(priority),
> + th->tid);
> +
> + return 0;
> +
> +error:
> + DosCloseEventSem (th->cancel_event);
> + DosCloseEventSem (th->done_event);
> + free (th);
> +
> + return ENOMEM;
> +}
> +
> +int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),
> + void *data, int priority)
> +{
> + return vlc_clone_attr (p_handle, false, entry, data, priority);
> +}
> +
> +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 );
> +
> + free( th );
> +}
> +
> +int vlc_clone_detach (vlc_thread_t *p_handle, void *(*entry) (void *),
> + void *data, int priority)
> +{
> + vlc_thread_t th;
> + if (p_handle == NULL)
> + p_handle = &th;
> +
> + return vlc_clone_attr (p_handle, true, entry, data, priority);
> +}
> +
> +int vlc_set_priority (vlc_thread_t th, int priority)
> +{
> + if (DosSetPriority(PRTYS_THREAD,
> + HIBYTE(priority),
> + LOBYTE(priority),
> + th->tid))
> + return VLC_EGENERIC;
> + return VLC_SUCCESS;
> +}
> +
> +/*** Thread cancellation ***/
> +
> +/* APC procedure for thread cancellation */
> +static void vlc_cancel_self (PVOID self)
> +{
> + struct vlc_thread *th = self;
> +
> + if (likely(th != NULL))
> + th->killed = true;
> +}
> +
> +void vlc_cancel (vlc_thread_t thread_id)
> +{
> + DosPostEventSem( thread_id->cancel_event );
> +}
> +
> +int vlc_savecancel (void)
> +{
> + int state;
> +
> + struct vlc_thread *th = vlc_threadvar_get (thread_key);
> + 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 = vlc_threadvar_get (thread_key);
> + 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 = vlc_threadvar_get (thread_key);
> + if (th == NULL)
> + return; /* Main thread - cannot be cancelled anyway */
> +
> + /* 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( NULL );
> +
> + if (th->killable && th->killed)
> + {
> + for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
> + p->proc (p->data);
> +
> + DosPostEventSem( th->done_event );
> + th->data = NULL; /* TODO: special value? */
> + vlc_thread_cleanup (th);
> + _endthread();
> + }
> +}
> +
> +void vlc_control_cancel (int cmd, ...)
> +{
> + /* NOTE: This function only modifies thread-specific data, so there is
> no + * need to lock anything. */
> + va_list ap;
> +
> + struct vlc_thread *th = vlc_threadvar_get (thread_key);
> + if (th == NULL)
> + return; /* Main thread - cannot be cancelled anyway */
> +
> + va_start (ap, cmd);
> + switch (cmd)
> + {
> + case VLC_CLEANUP_PUSH:
> + {
> + /* cleaner is a pointer to the caller stack, no need to
> allocate + * and copy anything. As a nice side effect, this
> cannot fail. */ + vlc_cleanup_t *cleaner = va_arg (ap,
> vlc_cleanup_t *); + cleaner->next = th->cleaners;
> + th->cleaners = cleaner;
> + break;
> + }
> +
> + case VLC_CLEANUP_POP:
> + {
> + th->cleaners = th->cleaners->next;
> + break;
> + }
> + }
> + va_end (ap);
> +}
> +
> +#define Q2LL( q ) ( *( long long * )&( q ))
> +
> +/*** Clock ***/
> +mtime_t mdate (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 (d.quot * 1000000) + ((d.rem * 1000000) / freq);
> +}
> +
> +#undef mwait
> +void mwait (mtime_t deadline)
> +{
> + mtime_t delay;
> +
> + vlc_testcancel();
> + while ((delay = (deadline - mdate())) > 0)
> + {
> + delay /= 1000;
> + if (unlikely(delay > 0x7fffffff))
> + delay = 0x7fffffff;
> + vlc_Sleep (delay);
> + vlc_testcancel();
> + }
> +}
> +
> +#undef msleep
> +void msleep (mtime_t delay)
> +{
> + mwait (mdate () + 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,
> + mtime_t value, mtime_t interval)
> +{
> + if (timer->htimer != NULLHANDLE)
> + {
> + DosStopTimer (timer->htimer);
> + timer->htimer = NULLHANDLE;
> + timer->interval = 0;
> + }
> +
> + if (value == 0)
> + return; /* Disarm */
> +
> + if (absolute)
> + value -= mdate ();
> + value = (value + 999) / 1000;
> + interval = (interval + 999) / 1000;
> +
> + timer->interval = interval;
> + if (DosAsyncTimer (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;
> +}
> +
--
Rémi Denis-Courmont
http://www.remlab.net/
http://fi.linkedin.com/in/remidenis
More information about the vlc-devel
mailing list