<html><head></head><body>Hi,<br><br>Where is the atomic-wait algorithm from? It's entirely clear to me if it's correct.<br><br><br><div class="gmail_quote">Le 20 janvier 2021 11:49:23 GMT+02:00, KO Myung-Hun <komh78@gmail.com> a écrit :<blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<pre class="k9mail"><hr> src/os2/thread.c | 351 ++++++++++++++++++++++++++++++++++++++++-------<br> 1 file changed, 302 insertions(+), 49 deletions(-)<br><br>diff --git a/src/os2/thread.c b/src/os2/thread.c<br>index 0d7bc9e79f..1c4774f04c 100644<br>--- a/src/os2/thread.c<br>+++ b/src/os2/thread.c<br>@@ -39,6 +39,9 @@<br> #include <errno.h><br> #include <time.h><br> <br>+#include <stdalign.h><br>+#include <stdatomic.h><br>+<br> #include <sys/types.h><br> #ifdef HAVE_SYS_SOCKET_H<br> #include <sys/socket.h><br>@@ -51,6 +54,11 @@<br> <br> #include <sys/stat.h><br> <br>+/* Static mutex and condition variable */<br>+static vlc_mutex_t super_mutex;<br>+static vlc_cond_t  super_variable;<br>+<br>+/* Threads */<br> static vlc_threadvar_t thread_key;<br> <br> struct vlc_thread<br>@@ -61,11 +69,17 @@ struct vlc_thread<br>     int            cancel_sock;<br> <br>     bool           killable;<br>-    bool           killed;<br>+    atomic_bool    killed;<br>     vlc_cleanup_t *cleaners;<br> <br>     void        *(*entry) (void *);<br>     void          *data;<br>+<br>+    struct<br>+    {<br>+        atomic_uint *addr;<br>+        HMTX        lock;<br>+    } wait;<br> };<br> <br> static void vlc_cancel_self (PVOID dummy);<br>@@ -129,40 +143,6 @@ static ULONG vlc_Sleep (ULONG ulTimeout)<br>     return ( rc != ERROR_TIMEOUT ) ? rc : 0;<br> }<br> <br>-static vlc_mutex_t super_mutex;<br>-static vlc_cond_t  super_variable;<br>-<br>-int _CRT_init(void);<br>-void _CRT_term(void);<br>-<br>-unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);<br>-<br>-unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)<br>-{<br>-    VLC_UNUSED (hmod);<br>-<br>-    switch (flag)<br>-    {<br>-        case 0 :    /* Initialization */<br>-            if(_CRT_init() == -1)<br>-                return 0;<br>-<br>-            vlc_mutex_init (&super_mutex);<br>-            vlc_cond_init (&super_variable);<br>-            vlc_threadvar_create (&thread_key, NULL);<br>-<br>-            return 1;<br>-<br>-        case 1 :    /* Termination */<br>-            vlc_threadvar_delete (&thread_key);<br>-<br>-            _CRT_term();<br>-<br>-            return 1;<br>-    }<br>-<br>-    return 0;   /* Failed */<br>-}<br> <br> /*** Thread-specific variables (TLS) ***/<br> struct vlc_threadvar<br>@@ -232,6 +212,197 @@ void *vlc_threadvar_get (vlc_threadvar_t key)<br>     return ( void * )*key->id;<br> }<br> <br>+static struct wait_bucket<br>+{<br>+    HMTX lock;<br>+    HEV wait;<br>+    unsigned waiters;<br>+} wait_buckets[ 32 ];<br>+<br>+static void wait_bucket_init( void )<br>+{<br>+    for( size_t i = 0; i < ARRAY_SIZE( wait_buckets ); i++ )<br>+    {<br>+        struct wait_bucket *bucket = wait_buckets + i;<br>+<br>+        DosCreateMutexSem( NULL, &bucket->lock, 0L, FALSE );<br>+        DosCreateEventSem( NULL, &bucket->wait, 0L, FALSE );<br>+    }<br>+}<br>+<br>+static void wait_bucket_destroy( void )<br>+{<br>+    for( size_t i = 0; i < ARRAY_SIZE( wait_buckets ); i++ )<br>+    {<br>+        struct wait_bucket *bucket = wait_buckets + i;<br>+<br>+        DosCloseMutexSem( bucket->lock );<br>+        DosCloseEventSem( bucket->wait );<br>+    }<br>+}<br>+<br>+static struct wait_bucket *wait_bucket_get( atomic_uint *addr )<br>+{<br>+    uintptr_t u = ( uintptr_t )addr;<br>+    size_t idx = ( u / alignof ( *addr )) % ARRAY_SIZE( wait_buckets );<br>+<br>+    return &wait_buckets[ idx ];<br>+}<br>+<br>+static struct wait_bucket *wait_bucket_enter( atomic_uint *addr )<br>+{<br>+    struct wait_bucket *bucket = wait_bucket_get(addr);<br>+<br>+    DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );<br>+    bucket->waiters++;<br>+<br>+    return bucket;<br>+}<br>+<br>+static void wait_bucket_leave( void *data )<br>+{<br>+    struct wait_bucket *bucket = data;<br>+<br>+    bucket->waiters--;<br>+    DosReleaseMutexSem( bucket->lock );<br>+}<br>+<br>+void vlc_atomic_wait( void *addr, unsigned value )<br>+{<br>+    atomic_uint *futex = addr;<br>+    struct wait_bucket *bucket = wait_bucket_enter( futex );<br>+<br>+    vlc_cleanup_push( wait_bucket_leave, bucket );<br>+<br>+    if( value == atomic_load_explicit( futex, memory_order_relaxed ))<br>+    {<br>+        ULONG count;<br>+<br>+        DosReleaseMutexSem( bucket->lock );<br>+        vlc_WaitForSingleObject( bucket->wait, SEM_INDEFINITE_WAIT );<br>+        DosResetEventSem( bucket->wait, &count );<br>+        DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );<br>+    }<br>+    else<br>+        vlc_testcancel();<br>+<br>+    wait_bucket_leave( bucket );<br>+    vlc_cleanup_pop();<br>+}<br>+<br>+int vlc_atomic_timedwait(void *addr, unsigned value, vlc_tick_t deadline)<br>+{<br>+    atomic_uint *futex = addr;<br>+    struct wait_bucket *bucket = wait_bucket_enter( futex );<br>+<br>+    ULONG rc = 0;<br>+<br>+    vlc_cleanup_push( wait_bucket_leave, bucket );<br>+<br>+    if( value == atomic_load_explicit( futex, memory_order_relaxed ))<br>+    {<br>+        vlc_tick_t delay;<br>+<br>+        DosReleaseMutexSem( bucket->lock );<br>+<br>+        do<br>+        {<br>+            ULONG ms;<br>+            ULONG count;<br>+<br>+            delay = deadline - vlc_tick_now();<br>+<br>+            if( delay < 0 )<br>+                ms = 0;<br>+            else if( delay >= VLC_TICK_FROM_MS( LONG_MAX ))<br>+                ms = LONG_MAX;<br>+            else<br>+                ms = MS_FROM_VLC_TICK( delay );<br>+<br>+            rc = vlc_WaitForSingleObject( bucket->wait, ms );<br>+            if( rc == 0 )<br>+            {<br>+                DosResetEventSem( bucket->wait, &count );<br>+                break;<br>+            }<br>+        } while( delay > 0 );<br>+<br>+        DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );<br>+    }<br>+    else<br>+        vlc_testcancel();<br>+<br>+    wait_bucket_leave( bucket );<br>+    vlc_cleanup_pop();<br>+<br>+    return rc == 0 ? 0 : ETIMEDOUT;<br>+}<br>+<br>+int vlc_atomic_timedwait_daytime(void *addr, unsigned value, time_t deadline)<br>+{<br>+    atomic_uint *futex = addr;<br>+    struct wait_bucket *bucket = wait_bucket_enter( futex );<br>+<br>+    ULONG rc = 0;<br>+<br>+    vlc_cleanup_push( wait_bucket_leave, bucket );<br>+<br>+    if( value == atomic_load_explicit( futex, memory_order_relaxed ))<br>+    {<br>+        vlc_tick_t delay;<br>+<br>+        DosReleaseMutexSem( bucket->lock );<br>+<br>+        do<br>+        {<br>+            ULONG ms;<br>+            ULONG count;<br>+<br>+            delay = deadline - time( NULL );<br>+<br>+            if( delay < 0 )<br>+                ms = 0;<br>+            else if( delay >= ( LONG_MAX / 1000 ))<br>+                ms = LONG_MAX;<br>+            else<br>+                ms = delay * 1000;<br>+<br>+            rc = vlc_WaitForSingleObject( bucket->wait, ms );<br>+            if( rc == 0 )<br>+            {<br>+                DosResetEventSem( bucket->wait, &count );<br>+                break;<br>+            }<br>+        } while( delay > 0 );<br>+<br>+        DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );<br>+    }<br>+    else<br>+        vlc_testcancel();<br>+<br>+    wait_bucket_leave( bucket );<br>+    vlc_cleanup_pop();<br>+<br>+    return rc == 0 ? 0 : ETIMEDOUT;<br>+}<br>+<br>+void vlc_atomic_notify_one(void *addr)<br>+{<br>+    vlc_atomic_notify_all(addr);<br>+}<br>+<br>+void vlc_atomic_notify_all(void *addr)<br>+{<br>+    struct wait_bucket *bucket = wait_bucket_get(addr);<br>+<br>+    DosRequestMutexSem( bucket->lock, SEM_INDEFINITE_WAIT );<br>+<br>+    if( bucket->waiters > 0 )<br>+        DosPostEventSem( bucket->wait );<br>+<br>+    DosReleaseMutexSem( bucket->lock);<br>+}<br>+<br> <br> /*** Threads ***/<br> void vlc_threads_setup (libvlc_int_t *p_libvlc)<br>@@ -280,8 +451,9 @@ int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),<br>     th->entry = entry;<br>     th->data = data;<br>     th->killable = false; /* not until vlc_entry() ! */<br>-    th->killed = false;<br>+    atomic_init (&th->killed, false);<br>     th->cleaners = NULL;<br>+    th->wait.addr = NULL;<br> <br>     if( DosCreateEventSem (NULL, &th->cancel_event, 0, FALSE))<br>         goto error;<br>@@ -292,6 +464,9 @@ int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),<br>     if( th->cancel_sock < 0 )<br>         goto error;<br> <br>+    if( DosCreateMutexSem (NULL, &th->wait.lock, 0, FALSE))<br>+        goto error;<br>+<br>     th->tid = _beginthread (vlc_entry, NULL, 1024 * 1024, th);<br>     if((int)th->tid == -1)<br>         goto error;<br>@@ -308,6 +483,7 @@ int vlc_clone (vlc_thread_t *p_handle, void *(*entry) (void *),<br>     return 0;<br> <br> error:<br>+    DosCloseMutexSem (th->wait.lock);<br>     soclose (th->cancel_sock);<br>     DosCloseEventSem (th->cancel_event);<br>     DosCloseEventSem (th->done_event);<br>@@ -334,6 +510,8 @@ void vlc_join (vlc_thread_t th, void **result)<br> <br>     soclose( th->cancel_sock );<br> <br>+    DosCloseMutexSem( th->wait.lock );<br>+<br>     free( th );<br> }<br> <br>@@ -360,13 +538,23 @@ static void vlc_cancel_self (PVOID self)<br>     struct vlc_thread *th = self;<br> <br>     if (likely(th != NULL))<br>-        th->killed = true;<br>+        atomic_store_explicit (&th->killed, true, memory_order_relaxed);<br> }<br> <br>-void vlc_cancel (vlc_thread_t thread_id)<br>+void vlc_cancel (vlc_thread_t th)<br> {<br>-    DosPostEventSem( thread_id->cancel_event );<br>-    so_cancel( thread_id->cancel_sock );<br>+    atomic_store_explicit( &th->killed, true, memory_order_relaxed );<br>+<br>+    DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );<br>+    if( th->wait.addr != NULL )<br>+    {<br>+        atomic_fetch_or_explicit( th->wait.addr, 1, memory_order_relaxed );<br>+        vlc_atomic_notify_all( th->wait.addr );<br>+    }<br>+    DosReleaseMutexSem( th->wait.lock );<br>+<br>+    DosPostEventSem( th->cancel_event );<br>+    so_cancel( th->cancel_sock );<br> }<br> <br> int vlc_savecancel (void)<br>@@ -400,21 +588,26 @@ void vlc_testcancel (void)<br>     if (th == NULL)<br>         return; /* Main thread - cannot be cancelled anyway */<br> <br>+    if (!th->killable)<br>+        return;<br>+<br>     /* This check is needed for the case that vlc_cancel() is followed by<br>      * vlc_testcancel() without any cancellation point */<br>     if( DosWaitEventSem( th->cancel_event, 0 ) == NO_ERROR )<br>         vlc_cancel_self( th );<br> <br>-    if (th->killable && th->killed)<br>-    {<br>-        for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)<br>-             p->proc (p->data);<br>+    if( !atomic_load_explicit( &th->killed, memory_order_relaxed ))<br>+        return;<br> <br>-        DosPostEventSem( th->done_event );<br>-        th->data = NULL; /* TODO: special value? */<br>-        vlc_thread_cleanup (th);<br>-        _endthread();<br>-    }<br>+    th->killable = true; /* Do not re-enter cancellation cleanup */<br>+<br>+    for (vlc_cleanup_t *p = th->cleaners; p != NULL; p = p->next)<br>+         p->proc (p->data);<br>+<br>+    DosPostEventSem( th->done_event );<br>+    th->data = NULL; /* TODO: special value? */<br>+    vlc_thread_cleanup (th);<br>+    _endthread();<br> }<br> <br> void vlc_control_cancel (vlc_cleanup_t *cleaner)<br>@@ -439,6 +632,30 @@ void vlc_control_cancel (vlc_cleanup_t *cleaner)<br>     }<br> }<br> <br>+void vlc_cancel_addr_set( atomic_uint *addr )<br>+{<br>+    struct vlc_thread *th = vlc_threadvar_get( thread_key );<br>+    if( th == NULL )<br>+        return; /* Main thread - cannot be cancelled anyway */<br>+<br>+    DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );<br>+    assert( th->wait.addr == NULL );<br>+    th->wait.addr = addr;<br>+    DosReleaseMutexSem( th->wait.lock );<br>+}<br>+<br>+void vlc_cancel_addr_clear( atomic_uint *addr )<br>+{<br>+    struct vlc_thread *th = vlc_threadvar_get( thread_key );<br>+    if( th == NULL )<br>+        return; /* Main thread - cannot be cancelled anyway */<br>+<br>+    DosRequestMutexSem( th->wait.lock, SEM_INDEFINITE_WAIT );<br>+    assert( th->wait.addr == addr );<br>+    th->wait.addr = NULL;<br>+    DosReleaseMutexSem( th->wait.lock );<br>+}<br>+<br> static int vlc_select( int nfds, fd_set *rdset, fd_set *wrset, fd_set *exset,<br>                        struct timeval *timeout )<br> {<br>@@ -705,3 +922,39 @@ unsigned vlc_GetCPUCount (void)<br> <br>     return numprocs;<br> }<br>+<br>+int _CRT_init(void);<br>+void _CRT_term(void);<br>+<br>+unsigned long _System _DLL_InitTerm(unsigned long, unsigned long);<br>+<br>+unsigned long _System _DLL_InitTerm(unsigned long hmod, unsigned long flag)<br>+{<br>+    VLC_UNUSED (hmod);<br>+<br>+    switch (flag)<br>+    {<br>+        case 0 :    /* Initialization */<br>+            if(_CRT_init() == -1)<br>+                return 0;<br>+<br>+            wait_bucket_init();<br>+<br>+            vlc_mutex_init (&super_mutex);<br>+            vlc_cond_init (&super_variable);<br>+            vlc_threadvar_create (&thread_key, NULL);<br>+<br>+            return 1;<br>+<br>+        case 1 :    /* Termination */<br>+            vlc_threadvar_delete (&thread_key);<br>+<br>+            wait_bucket_destroy();<br>+<br>+            _CRT_term();<br>+<br>+            return 1;<br>+    }<br>+<br>+    return 0;   /* Failed */<br>+}</pre></blockquote></div><br>-- <br>Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté.</body></html>