[vlc-devel] [PATCH 5/6] os2: thread: implement vlc_cond_signal() correctly

KO Myung-Hun komh78 at gmail.com
Wed Jun 10 05:27:28 CEST 2015


---
 include/vlc_threads.h |   5 +-
 src/os2/thread.c      | 123 +++++++++++++++++++++++++++++++-------------------
 2 files changed, 81 insertions(+), 47 deletions(-)

diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index 38bc3a7..f3ac928 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -111,9 +111,12 @@ typedef struct
 typedef struct
 {
     HEV      hev;
+    unsigned waiters;
+    HEV      hevAck;
+    unsigned signaled;
     unsigned clock;
 } vlc_cond_t;
-#define VLC_STATIC_COND { 0, 0 }
+#define VLC_STATIC_COND { NULLHANDLE, 0, NULLHANDLE, 0, 0 }
 #define LIBVLC_NEED_SEMAPHORE
 #define LIBVLC_NEED_RWLOCK
 typedef struct vlc_threadvar *vlc_threadvar_t;
diff --git a/src/os2/thread.c b/src/os2/thread.c
index da191ac..3d430ea 100644
--- a/src/os2/thread.c
+++ b/src/os2/thread.c
@@ -45,6 +45,8 @@
 #include <sys/time.h>
 #include <sys/select.h>
 
+#include <386/builtin.h>
+
 static vlc_threadvar_t thread_key;
 
 /**
@@ -261,9 +263,12 @@ enum
 
 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))
+    if (DosCreateEventSem (NULL, &p_condvar->hev, 0, FALSE) ||
+        DosCreateEventSem (NULL, &p_condvar->hevAck, 0, FALSE))
         abort();
+
+    p_condvar->waiters = 0;
+    p_condvar->signaled = 0;
     p_condvar->clock = clock;
 }
 
@@ -280,6 +285,7 @@ void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
 void vlc_cond_destroy (vlc_cond_t *p_condvar)
 {
     DosCloseEventSem( p_condvar->hev );
+    DosCloseEventSem( p_condvar->hevAck );
 }
 
 void vlc_cond_signal (vlc_cond_t *p_condvar)
@@ -287,8 +293,28 @@ void vlc_cond_signal (vlc_cond_t *p_condvar)
     if (!p_condvar->hev)
         return;
 
-    /* This is suboptimal but works. */
-    vlc_cond_broadcast (p_condvar);
+    if (!__atomic_cmpxchg32 (&p_condvar->waiters, 0, 0))
+    {
+        ULONG ulPost;
+
+        __atomic_xchg (&p_condvar->signaled, 1);
+        DosPostEventSem (p_condvar->hev);
+
+        DosWaitEventSem (p_condvar->hevAck, SEM_INDEFINITE_WAIT);
+        DosResetEventSem (p_condvar->hevAck, &ulPost);
+
+        while (ulPost-- > 0)
+            __atomic_decrement (&p_condvar->waiters);
+
+        /* Already timed out ? */
+        if (__atomic_cmpxchg32 (&p_condvar->waiters, 0, 0) &&
+            __atomic_cmpxchg32 (&p_condvar->signaled, 1, 1))
+        {
+            /* Clear signaled status */
+            __atomic_xchg (&p_condvar->signaled, 0);
+            DosResetEventSem (p_condvar->hev, &ulPost);
+        }
+    }
 }
 
 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
@@ -296,39 +322,55 @@ 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 );
+    while (!__atomic_cmpxchg32 (&p_condvar->waiters, 0, 0))
+        vlc_cond_signal (p_condvar);
 }
 
-void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
+static int vlc_cond_wait_common (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
+                                 ULONG ulTimeout)
 {
     ULONG ulPost;
     ULONG rc;
 
-    if (!p_condvar->hev)
-    {   /* FIXME FIXME FIXME */
-        msleep (50000);
-        return;
-    }
+    __atomic_increment (&p_condvar->waiters);
 
     do
     {
         vlc_testcancel();
 
         vlc_mutex_unlock (p_mutex);
-        rc = vlc_WaitForSingleObject( p_condvar->hev, SEM_INDEFINITE_WAIT );
+
+        do
+        {
+            rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
+            if (rc == NO_ERROR)
+                DosResetEventSem (p_condvar->hev, &ulPost);
+        } while (rc == NO_ERROR &&
+                 __atomic_cmpxchg32 (&p_condvar->signaled, 0, 1) == 0);
+
+        DosPostEventSem (p_condvar->hevAck);
+
         vlc_mutex_lock (p_mutex);
     } while( rc == ERROR_INTERRUPT );
 
-    DosResetEventSem( p_condvar->hev, &ulPost );
+    return rc ? ETIMEDOUT : 0;
+}
+
+void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
+{
+    if (!p_condvar->hev)
+    {   /* FIXME FIXME FIXME */
+        msleep (50000);
+        return;
+    }
+
+    vlc_cond_wait_common (p_condvar, p_mutex, SEM_INDEFINITE_WAIT);
 }
 
 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 */
@@ -336,41 +378,30 @@ int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
         return 0;
     }
 
-    do
+    mtime_t total;
+    switch (p_condvar->clock)
     {
-        vlc_testcancel();
-
-        mtime_t total;
-        switch (p_condvar->clock)
+        case CLOCK_REALTIME:
         {
-            case CLOCK_REALTIME:
-            {
-                struct timeval tv;
-                gettimeofday (&tv, NULL);
-
-                total = CLOCK_FREQ * tv.tv_sec +
-                        CLOCK_FREQ * tv.tv_usec / 1000000L;
-                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;
+            struct timeval tv;
+            gettimeofday (&tv, NULL);
 
-        vlc_mutex_unlock (p_mutex);
-        rc = vlc_WaitForSingleObject( p_condvar->hev, ulTimeout );
-        vlc_mutex_lock (p_mutex);
-    } while( rc == ERROR_INTERRUPT );
+            total = CLOCK_FREQ * tv.tv_sec +
+                    CLOCK_FREQ * tv.tv_usec / 1000000L;
+            break;
+        }
+        default:
+            assert (p_condvar->clock == CLOCK_MONOTONIC);
+            total = mdate();
+            break;
+    }
+    total = (deadline - total) / 1000;
+    if( total < 0 )
+        total = 0;
 
-    DosResetEventSem( p_condvar->hev, &ulPost );
+    ulTimeout = ( total > 0x7fffffff ) ? 0x7fffffff : total;
 
-    return rc ? ETIMEDOUT : 0;
+    return vlc_cond_wait_common (p_condvar, p_mutex, ulTimeout);
 }
 
 /*** Thread-specific variables (TLS) ***/
-- 
1.9.5




More information about the vlc-devel mailing list