[vlc-devel] [PATCH 01/11] os2: thread: implement missing threading functions

Rémi Denis-Courmont remi at remlab.net
Mon Jan 25 11:53:35 UTC 2021


Hi,

Where is the atomic-wait algorithm from? It's entirely clear to me if it's correct.


Le 20 janvier 2021 11:49:23 GMT+02:00, KO Myung-Hun <komh78 at gmail.com> a écrit :
>---
> src/os2/thread.c | 351 ++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 302 insertions(+), 49 deletions(-)
>
>diff --git a/src/os2/thread.c b/src/os2/thread.c
>index 0d7bc9e79f..1c4774f04c 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
>@@ -61,11 +69,17 @@ struct vlc_thread
>     int            cancel_sock;
> 
>     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);
>@@ -129,40 +143,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;
>-
>-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);
>-
>-            return 1;
>-
>-        case 1 :    /* Termination */
>-            vlc_threadvar_delete (&thread_key);
>-
>-            _CRT_term();
>-
>-            return 1;
>-    }
>-
>-    return 0;   /* Failed */
>-}
> 
> /*** Thread-specific variables (TLS) ***/
> struct vlc_threadvar
>@@ -232,6 +212,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)
>@@ -280,8 +451,9 @@ int vlc_clone (vlc_thread_t *p_handle, void
>*(*entry) (void *),
>     th->entry = entry;
>     th->data = data;
>     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;
>@@ -292,6 +464,9 @@ int vlc_clone (vlc_thread_t *p_handle, void
>*(*entry) (void *),
>     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;
>@@ -308,6 +483,7 @@ int vlc_clone (vlc_thread_t *p_handle, void
>*(*entry) (void *),
>     return 0;
> 
> error:
>+    DosCloseMutexSem (th->wait.lock);
>     soclose (th->cancel_sock);
>     DosCloseEventSem (th->cancel_event);
>     DosCloseEventSem (th->done_event);
>@@ -334,6 +510,8 @@ void vlc_join (vlc_thread_t th, void **result)
> 
>     soclose( th->cancel_sock );
> 
>+    DosCloseMutexSem( th->wait.lock );
>+
>     free( th );
> }
> 
>@@ -360,13 +538,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)
>@@ -400,21 +588,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)
>@@ -439,6 +632,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 )
> {
>@@ -705,3 +922,39 @@ unsigned vlc_GetCPUCount (void)
> 
>     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();
>+
>+            vlc_mutex_init (&super_mutex);
>+            vlc_cond_init (&super_variable);
>+            vlc_threadvar_create (&thread_key, NULL);
>+
>+            return 1;
>+
>+        case 1 :    /* Termination */
>+            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

-- 
Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20210125/d5e0b7ef/attachment.html>


More information about the vlc-devel mailing list