clock: use seqlocks to parallellize readers
authorWim Taymans <wim.taymans@collabora.co.uk>
Thu, 19 Mar 2009 10:37:12 +0000 (11:37 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Fri, 15 May 2009 14:14:52 +0000 (16:14 +0200)
docs/gst/gstreamer-sections.txt
gst/gstclock.c
gst/gstclock.h

index 1aacedf..e06b618 100644 (file)
@@ -414,6 +414,7 @@ GST_TYPE_CLOCK_FLAGS
 GST_TYPE_CLOCK_RETURN
 GST_TYPE_CLOCK_TIME
 <SUBSECTION Private>
+GstClockPrivate
 GST_CLOCK_SLAVE_LOCK
 GST_CLOCK_SLAVE_UNLOCK
 gst_clock_get_type
index afc3df8..8618f04 100644 (file)
@@ -132,6 +132,41 @@ enum
   PROP_TIMEOUT
 };
 
+struct _GstClockPrivate
+{
+  gint pre_count;
+  gint post_count;
+};
+
+/* seqlocks */
+#define read_seqbegin(clock)                                   \
+  g_atomic_int_get (&clock->priv->post_count);
+
+static inline gboolean
+read_seqretry (GstClock * clock, gint seq)
+{
+  /* no retry if the seqnum did not change */
+  if (G_LIKELY (seq == g_atomic_int_get (&clock->priv->pre_count)))
+    return FALSE;
+
+  /* wait for the writer to finish and retry */
+  GST_OBJECT_LOCK (clock);
+  GST_OBJECT_UNLOCK (clock);
+  return TRUE;
+}
+
+#define write_seqlock(clock)                      \
+G_STMT_START {                                    \
+  GST_OBJECT_LOCK (clock);                        \
+  g_atomic_int_inc (&clock->priv->pre_count);     \
+} G_STMT_END;
+
+#define write_sequnlock(clock)                    \
+G_STMT_START {                                    \
+  g_atomic_int_inc (&clock->priv->post_count);    \
+  GST_OBJECT_UNLOCK (clock);                      \
+} G_STMT_END;
+
 static void gst_clock_class_init (GstClockClass * klass);
 static void gst_clock_init (GstClock * clock);
 static void gst_clock_dispose (GObject * object);
@@ -404,7 +439,7 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
   if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
     entry->time = requested + entry->interval;
 
-  if (clock->stats)
+  if (G_UNLIKELY (clock->stats))
     gst_clock_update_stats (clock);
 
   return res;
@@ -560,6 +595,8 @@ gst_clock_class_init (GstClockClass * klass)
           "The amount of time, in nanoseconds, to sample master and slave clocks",
           0, G_MAXUINT64, DEFAULT_TIMEOUT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (klass, sizeof (GstClockPrivate));
 }
 
 static void
@@ -570,6 +607,9 @@ gst_clock_init (GstClock * clock)
   clock->entries_changed = g_cond_new ();
   clock->stats = FALSE;
 
+  clock->priv =
+      G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);
+
   clock->internal_calibration = 0;
   clock->external_calibration = 0;
   clock->rate_numerator = 1;
@@ -701,7 +741,7 @@ gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
   cdenom = clock->rate_denominator;
 
   /* avoid divide by 0 */
-  if (cdenom == 0)
+  if (G_UNLIKELY (cdenom == 0))
     cnum = cdenom = 1;
 
   /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
@@ -711,12 +751,14 @@ gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
    * though.
    */
   if (G_LIKELY (internal >= cinternal)) {
-    ret = gst_util_uint64_scale (internal - cinternal, cnum, cdenom);
+    ret = internal - cinternal;
+    ret = gst_util_uint64_scale (ret, cnum, cdenom);
     ret += cexternal;
   } else {
-    ret = gst_util_uint64_scale (cinternal - internal, cnum, cdenom);
+    ret = cinternal - internal;
+    ret = gst_util_uint64_scale (ret, cnum, cdenom);
     /* clamp to 0 */
-    if (cexternal > ret)
+    if (G_LIKELY (cexternal > ret))
       ret = cexternal - ret;
     else
       ret = 0;
@@ -756,16 +798,18 @@ gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
   cdenom = clock->rate_denominator;
 
   /* avoid divide by 0 */
-  if (cnum == 0)
+  if (G_UNLIKELY (cnum == 0))
     cnum = cdenom = 1;
 
   /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
-  if (external >= cexternal) {
-    ret = gst_util_uint64_scale (external - cexternal, cdenom, cnum);
+  if (G_LIKELY (external >= cexternal)) {
+    ret = external - cexternal;
+    ret = gst_util_uint64_scale (ret, cdenom, cnum);
     ret += cinternal;
   } else {
-    ret = gst_util_uint64_scale (cexternal - external, cdenom, cnum);
-    if (cinternal > ret)
+    ret = cexternal - external;
+    ret = gst_util_uint64_scale (ret, cdenom, cnum);
+    if (G_LIKELY (cinternal > ret))
       ret = cinternal - ret;
     else
       ret = 0;
@@ -831,15 +875,19 @@ GstClockTime
 gst_clock_get_time (GstClock * clock)
 {
   GstClockTime ret;
+  gint seq;
 
   g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
 
-  ret = gst_clock_get_internal_time (clock);
+  do {
+    /* reget the internal time when we retry to get the most current
+     * timevalue */
+    ret = gst_clock_get_internal_time (clock);
 
-  GST_OBJECT_LOCK (clock);
-  /* this will scale for rate and offset */
-  ret = gst_clock_adjust_unlocked (clock, ret);
-  GST_OBJECT_UNLOCK (clock);
+    seq = read_seqbegin (clock);
+    /* this will scale for rate and offset */
+    ret = gst_clock_adjust_unlocked (clock, ret);
+  } while (read_seqretry (clock, seq));
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
       GST_TIME_ARGS (ret));
@@ -889,7 +937,7 @@ gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
   g_return_if_fail (rate_denom > 0 && rate_denom != GST_CLOCK_TIME_NONE);
   g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
 
-  GST_OBJECT_LOCK (clock);
+  write_seqlock (clock);
   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
       "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT " %"
       G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f", GST_TIME_ARGS (internal),
@@ -900,7 +948,7 @@ gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
   clock->external_calibration = external;
   clock->rate_numerator = rate_num;
   clock->rate_denominator = rate_denom;
-  GST_OBJECT_UNLOCK (clock);
+  write_sequnlock (clock);
 }
 
 /**
@@ -923,18 +971,21 @@ void
 gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
     GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
 {
+  gint seq;
+
   g_return_if_fail (GST_IS_CLOCK (clock));
 
-  GST_OBJECT_LOCK (clock);
-  if (rate_num)
-    *rate_num = clock->rate_numerator;
-  if (rate_denom)
-    *rate_denom = clock->rate_denominator;
-  if (external)
-    *external = clock->external_calibration;
-  if (internal)
-    *internal = clock->internal_calibration;
-  GST_OBJECT_UNLOCK (clock);
+  do {
+    seq = read_seqbegin (clock);
+    if (rate_num)
+      *rate_num = clock->rate_numerator;
+    if (rate_denom)
+      *rate_denom = clock->rate_denominator;
+    if (external)
+      *external = clock->external_calibration;
+    if (internal)
+      *internal = clock->internal_calibration;
+  } while (read_seqretry (clock, seq));
 }
 
 /* will be called repeatedly to sample the master and slave clock
index 4ce1bcd..637d351 100644 (file)
@@ -235,6 +235,7 @@ G_STMT_START {                                              \
 typedef struct _GstClockEntry  GstClockEntry;
 typedef struct _GstClock       GstClock;
 typedef struct _GstClockClass  GstClockClass;
+typedef struct _GstClockPrivate        GstClockPrivate;
 
 /* --- prototype for async callbacks --- */
 /**
@@ -447,7 +448,9 @@ struct _GstClock {
   GstClockID     clockid;
 
   /*< private >*/
-  GstClockTime  _gst_reserved[GST_PADDING];
+  GstClockPrivate *priv;
+
+  GstClockTime  _gst_reserved[GST_PADDING-1];
 };
 
 /**