<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>