[vlc-devel] [PATCH 2/2] os2: thread: implemet missing threading functions
Alexandre Janniaux
ajanni at videolabs.io
Tue Mar 31 11:47:11 CEST 2020
Hi, just for info, typo in the commit message.
Regards,
--
Alexandre Janniaux
Videolabs
On Tue, Mar 31, 2020 at 06:17:15PM +0900, KO Myung-Hun wrote:
> ---
> src/os2/thread.c | 362 ++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 309 insertions(+), 53 deletions(-)
>
> diff --git a/src/os2/thread.c b/src/os2/thread.c
> index 66932bd2a1..6151ee57d5 100644
> --- a/src/os2/thread.c
> +++ b/src/os2/thread.c
> @@ -39,6 +39,9 @@
> #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>
> @@ -51,6 +54,11 @@
>
> #include <sys/stat.h>
>
> +/* Static mutex and condition variable */
> +static vlc_mutex_t super_mutex;
> +static vlc_cond_t super_variable;
> +
> +/* Threads */
> static vlc_threadvar_t thread_key;
>
> struct vlc_thread
> @@ -62,11 +70,17 @@ struct vlc_thread
>
> bool detached;
> bool killable;
> - bool killed;
> + atomic_bool killed;
> vlc_cleanup_t *cleaners;
>
> void *(*entry) (void *);
> void *data;
> +
> + struct
> + {
> + atomic_uint *addr;
> + HMTX lock;
> + } wait;
> };
>
> static void vlc_cancel_self (PVOID dummy);
> @@ -130,44 +144,6 @@ static ULONG vlc_Sleep (ULONG ulTimeout)
> return ( rc != ERROR_TIMEOUT ) ? rc : 0;
> }
>
> -static vlc_mutex_t super_mutex;
> -static vlc_cond_t super_variable;
> -extern vlc_rwlock_t config_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);
> -
> - return 1;
> -
> - case 1 : /* Termination */
> - vlc_rwlock_destroy (&config_lock);
> - vlc_threadvar_delete (&thread_key);
> -
> - _CRT_term();
> -
> - return 1;
> - }
> -
> - return 0; /* Failed */
> -}
> -
> void vlc_once(vlc_once_t *once, void (*cb)(void))
> {
> /* not initialized ? */
> @@ -256,6 +232,197 @@ 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;
> +}
> +
> +int vlc_atomic_timedwait_daytime(void *addr, unsigned value, time_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 - time( NULL );
> +
> + if( delay < 0 )
> + ms = 0;
> + else if( delay >= ( LONG_MAX / 1000 ))
> + ms = LONG_MAX;
> + else
> + ms = delay * 1000;
> +
> + 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)
> @@ -290,6 +457,8 @@ retry:
>
> soclose (th->cancel_sock);
>
> + DosCloseMutexSem (th->wait.lock);
> +
> free (th);
> }
> }
> @@ -315,8 +484,9 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
> th->data = data;
> th->detached = detached;
> th->killable = false; /* not until vlc_entry() ! */
> - th->killed = false;
> + atomic_init (&th->killed, false);
> th->cleaners = NULL;
> + th->wait.addr = NULL;
>
> if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))
> goto error;
> @@ -327,6 +497,9 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
> if( th->cancel_sock < 0 )
> goto error;
>
> + if( DosCreateMutexSem (NULL, &th->wait.lock, 0, FALSE))
> + goto error;
> +
> th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);
> if((int)th->tid == -1)
> goto error;
> @@ -343,6 +516,7 @@ static int vlc_clone_attr (vlc_thread_t *p_handle, bool detached,
> return 0;
>
> error:
> + DosCloseMutexSem (th->wait.lock);
> soclose (th->cancel_sock);
> DosCloseEventSem (th->cancel_event);
> DosCloseEventSem (th->done_event);
> @@ -375,6 +549,8 @@ void vlc_join (vlc_thread_t th, void **result)
>
> soclose( th->cancel_sock );
>
> + DosCloseMutexSem( th->wait.lock );
> +
> free( th );
> }
>
> @@ -411,13 +587,23 @@ static void vlc_cancel_self (PVOID self)
> struct vlc_thread *th = self;
>
> if (likely(th != NULL))
> - th->killed = true;
> + atomic_store_explicit (&th->killed, true, memory_order_relaxed);
> }
>
> -void vlc_cancel (vlc_thread_t thread_id)
> +void vlc_cancel (vlc_thread_t th)
> {
> - DosPostEventSem( thread_id->cancel_event );
> - so_cancel( thread_id->cancel_sock );
> + atomic_store_explicit( &th->killed, true, memory_order_relaxed );
> +
> + DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );
> + if( th->wait.addr != NULL )
> + {
> + atomic_fetch_or_explicit( th->wait.addr, 1, memory_order_relaxed );
> + vlc_atomic_notify_all( th->wait.addr );
> + }
> + DosReleaseMutexSem( th->wait.lock );
> +
> + DosPostEventSem( th->cancel_event );
> + so_cancel( th->cancel_sock );
> }
>
> int vlc_savecancel (void)
> @@ -451,21 +637,26 @@ void vlc_testcancel (void)
> 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 (th->killable && th->killed)
> - {
> - for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)
> - p->proc (p->data);
> + if( !atomic_load_explicit( &th->killed, memory_order_relaxed ))
> + return;
>
> - DosPostEventSem( th->done_event );
> - th->data = NULL; /* TODO: special value? */
> - vlc_thread_cleanup (th);
> - _endthread();
> - }
> + 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 = NULL; /* TODO: special value? */
> + vlc_thread_cleanup (th);
> + _endthread();
> }
>
> void vlc_control_cancel (vlc_cleanup_t *cleaner)
> @@ -490,6 +681,30 @@ void vlc_control_cancel (vlc_cleanup_t *cleaner)
> }
> }
>
> +void vlc_cancel_addr_set( atomic_uint *addr )
> +{
> + struct vlc_thread *th = vlc_threadvar_get( thread_key );
> + if( th == NULL )
> + return; /* Main thread - cannot be cancelled anyway */
> +
> + DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );
> + assert( th->wait.addr == NULL );
> + th->wait.addr = addr;
> + DosReleaseMutexSem( th->wait.lock );
> +}
> +
> +void vlc_cancel_addr_clear( atomic_uint *addr )
> +{
> + struct vlc_thread *th = vlc_threadvar_get( thread_key );
> + if( th == NULL )
> + return; /* Main thread - cannot be cancelled anyway */
> +
> + DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );
> + assert( th->wait.addr == addr );
> + th->wait.addr = NULL;
> + DosReleaseMutexSem( th->wait.lock );
> +}
> +
> static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,
> struct timeval *timeout )
> {
> @@ -756,3 +971,44 @@ unsigned vlc_GetCPUCount (void)
>
> return numprocs;
> }
> +
> +extern vlc_rwlock_t config_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;
> +
> + wait_bucket_init();
> +
> + vlc_mutex_init (&super_mutex);
> + vlc_cond_init (&super_variable);
> + vlc_threadvar_create (&thread_key, NULL);
> + vlc_rwlock_init (&config_lock);
> +
> + return 1;
> +
> + case 1 : /* Termination */
> + vlc_rwlock_destroy (&config_lock);
> + vlc_threadvar_delete (&thread_key);
> +
> + wait_bucket_destroy();
> +
> + _CRT_term();
> +
> + return 1;
> + }
> +
> + return 0; /* Failed */
> +}
> +
> --
> 2.22.0
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list