[vlc-commits] src: darwin/thread: reimplement monotonic clock for darwin

David Fuhrmann git at videolan.org
Tue Jan 7 09:50:17 CET 2014


vlc | branch: master | David Fuhrmann <david.fuhrmann at googlemail.com> | Tue Dec 31 11:41:55 2013 +0100| [15a85cc6cc73e8ca885bac7e72a3c22ae32ad54b] | committer: David Fuhrmann

src: darwin/thread: reimplement monotonic clock for darwin

This is based on code which was silently removed in
0f9a94f2183fc6c6e9ce8b77eb497aab160cbbbd and
45c2bc0811cc2d9fe8e11448a05742d7ff6da1d8.

The patch solves timing issues if the user changes the system time, for
instance while playback.

Another timing issue was pointed out if vlc_cond_init_with_daytime is used.
This remains unfixed, and is documented in a Fixme.

close #10072

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=15a85cc6cc73e8ca885bac7e72a3c22ae32ad54b
---

 include/vlc_threads.h |    8 +++--
 src/darwin/thread.c   |   96 ++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 86 insertions(+), 18 deletions(-)

diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index a515afd..00435cb 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -155,8 +155,12 @@ typedef struct vlc_timer *vlc_timer_t;
 typedef pthread_t       vlc_thread_t;
 typedef pthread_mutex_t vlc_mutex_t;
 #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
-typedef pthread_cond_t  vlc_cond_t;
-#define VLC_STATIC_COND  PTHREAD_COND_INITIALIZER
+typedef struct
+{
+    pthread_cond_t cond;
+    unsigned clock;
+} vlc_cond_t;
+#define VLC_STATIC_COND  { PTHREAD_COND_INITIALIZER, 0 }
 typedef semaphore_t     vlc_sem_t;
 typedef pthread_rwlock_t vlc_rwlock_t;
 #define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
diff --git a/src/darwin/thread.c b/src/darwin/thread.c
index b69e7f9..7f96ce6 100644
--- a/src/darwin/thread.c
+++ b/src/darwin/thread.c
@@ -39,11 +39,21 @@
 
 #include <pthread.h>
 #include <mach/mach_init.h> /* mach_task_self in semaphores */
+#include <mach/mach_time.h>
 #include <execinfo.h>
-#include <sys/time.h> /* gettimeofday() */
 
-#define vlc_clock_setup() (void)0
-#warning Monotonic clock not available. Expect timing issues.
+static mach_timebase_info_data_t vlc_clock_conversion_factor;
+
+static void vlc_clock_setup_once (void)
+{
+    if (unlikely(mach_timebase_info (&vlc_clock_conversion_factor) != 0))
+        abort ();
+}
+
+static pthread_once_t vlc_clock_once = PTHREAD_ONCE_INIT;
+
+#define vlc_clock_setup() \
+    pthread_once(&vlc_clock_once, vlc_clock_setup_once)
 
 static struct timespec mtime_to_ts (mtime_t date)
 {
@@ -213,6 +223,12 @@ void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
     VLC_THREAD_ASSERT ("unlocking mutex");
 }
 
+enum
+{
+    VLC_CLOCK_MONOTONIC = 0,
+    VLC_CLOCK_REALTIME,
+};
+
 /* Initialize a condition variable. */
 void vlc_cond_init (vlc_cond_t *p_condvar)
 {
@@ -221,9 +237,10 @@ void vlc_cond_init (vlc_cond_t *p_condvar)
     if (unlikely(pthread_condattr_init (&attr)))
         abort ();
 
-    if (unlikely(pthread_cond_init (p_condvar, &attr)))
+    if (unlikely(pthread_cond_init (&p_condvar->cond, &attr)))
         abort ();
     pthread_condattr_destroy (&attr);
+    p_condvar->clock = VLC_CLOCK_MONOTONIC;
 }
 
 /* Initialize a condition variable.
@@ -231,8 +248,10 @@ void vlc_cond_init (vlc_cond_t *p_condvar)
  * the vlc_cond_timedwait() time-out parameter. */
 void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
 {
-    if (unlikely(pthread_cond_init (p_condvar, NULL)))
+    if (unlikely(pthread_cond_init (&p_condvar->cond, NULL)))
         abort ();
+    p_condvar->clock = VLC_CLOCK_REALTIME;
+
 }
 
 /* Destroys a condition variable. No threads shall be waiting or signaling the
@@ -240,7 +259,7 @@ void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
  * parameter: p_condvar condition variable to destroy */
 void vlc_cond_destroy (vlc_cond_t *p_condvar)
 {
-    int val = pthread_cond_destroy( p_condvar );
+    int val = pthread_cond_destroy (&p_condvar->cond);
 
     /* due to a faulty pthread implementation within Darwin 11 and
      * later condition variables cannot be destroyed without
@@ -270,7 +289,7 @@ void vlc_cond_destroy (vlc_cond_t *p_condvar)
  * parameter: p_condvar condition variable */
 void vlc_cond_signal (vlc_cond_t *p_condvar)
 {
-    int val = pthread_cond_signal( p_condvar );
+    int val = pthread_cond_signal (&p_condvar->cond);
     VLC_THREAD_ASSERT ("signaling condition variable");
 }
 
@@ -278,7 +297,7 @@ void vlc_cond_signal (vlc_cond_t *p_condvar)
  * parameter: p_cond condition variable */
 void vlc_cond_broadcast (vlc_cond_t *p_condvar)
 {
-    pthread_cond_broadcast (p_condvar);
+    pthread_cond_broadcast (&p_condvar->cond);
 }
 
 /* Wait for a condition variable. The calling thread will be suspended until
@@ -311,7 +330,7 @@ void vlc_cond_broadcast (vlc_cond_t *p_condvar)
  *                then locked again when waking up. */
 void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
 {
-    int val = pthread_cond_wait( p_condvar, p_mutex );
+    int val = pthread_cond_wait (&p_condvar->cond, p_mutex);
     VLC_THREAD_ASSERT ("waiting on condition");
 }
 
@@ -332,8 +351,44 @@ void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
 int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
                         mtime_t deadline)
 {
-    struct timespec ts = mtime_to_ts (deadline);
-    int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
+    int val = 0;
+
+    /*
+     * Note that both pthread_cond_timedwait_relative_np and pthread_cond_timedwait
+     * convert the given timeout to a mach absolute deadline, with system startup
+     * as the time origin. There is no way you can change this behaviour.
+     *
+     * For more details, see: https://devforums.apple.com/message/931605
+     */
+
+    if (p_condvar->clock == VLC_CLOCK_MONOTONIC) {
+
+        /* 
+         * mdate() is the monotonic clock, pthread_cond_timedwait expects
+         * origin of gettimeofday(). Use timedwait_relative_np() instead.
+         */
+        mtime_t base = mdate();
+        deadline -= base;
+        if (deadline < 0)
+            deadline = 0;
+        struct timespec ts = mtime_to_ts(deadline);
+
+        val = pthread_cond_timedwait_relative_np(&p_condvar->cond, p_mutex, &ts);
+
+    } else {
+        /* variant for vlc_cond_init_daytime */
+        assert (p_condvar->clock == VLC_CLOCK_REALTIME);
+
+        /* 
+         * FIXME: It is assumed, that in this case the system waits until the real
+         * time deadline is passed, even if the real time is adjusted in between.
+         * This is not fulfilled, as described above.
+         */
+        struct timespec ts = mtime_to_ts(deadline);
+
+        val = pthread_cond_timedwait(&p_condvar->cond, p_mutex, &ts);
+    }
+    
     if (val != ETIMEDOUT)
         VLC_THREAD_ASSERT ("timed-waiting on condition");
     return val;
@@ -698,11 +753,19 @@ void vlc_control_cancel (int cmd, ...)
  * returns a timestamp in microseconds. */
 mtime_t mdate (void)
 {
-    struct timeval tv;
+    vlc_clock_setup();
+    uint64_t date = mach_absolute_time();
 
-    if (unlikely(gettimeofday (&tv, NULL) != 0))
-        abort ();
-    return (INT64_C(1000000) * tv.tv_sec) + tv.tv_usec;
+    /* denom is uint32_t, switch to 64 bits to prevent overflow. */
+    uint64_t denom = vlc_clock_conversion_factor.denom;
+
+    /* Switch to microsecs */
+    denom *= 1000LL;
+
+    /* Split the division to prevent overflow */
+    lldiv_t d = lldiv (vlc_clock_conversion_factor.numer, denom);
+
+    return (d.quot * date) + ((d.rem * date) / denom);
 }
 
 #undef mwait
@@ -722,11 +785,12 @@ void msleep (mtime_t delay)
 {
     struct timespec ts = mtime_to_ts (delay);
 
+    /* nanosleep uses mach_absolute_time and mach_wait_until internally,
+       but also handles kernel errors. Thus we use just this. */
     while (nanosleep (&ts, &ts) == -1)
         assert (errno == EINTR);
 }
 
-
 /* Count CPUs.
  * returns the number of available (logical) CPUs. */
 unsigned vlc_GetCPUCount(void)



More information about the vlc-commits mailing list