gstpad: Probes that return HANDLED can reset the data info field
[platform/upstream/gstreamer.git] / gst / gstsystemclock.c
index 8508166..73dbfb4 100644 (file)
@@ -22,6 +22,7 @@
 
 /**
  * SECTION:gstsystemclock
+ * @title: GstSystemClock
  * @short_description: Default clock that uses the current system time
  * @see_also: #GstClock
  *
@@ -88,16 +89,13 @@ struct _GstSystemClockPrivate
 #ifdef G_OS_WIN32
   LARGE_INTEGER start;
   LARGE_INTEGER frequency;
+  guint64 ratio;
 #endif                          /* G_OS_WIN32 */
 #ifdef __APPLE__
   struct mach_timebase_info mach_timebase;
 #endif
 };
 
-#define GST_SYSTEM_CLOCK_GET_PRIVATE(obj)  \
-   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SYSTEM_CLOCK, \
-        GstSystemClockPrivate))
-
 #ifdef HAVE_POSIX_TIMERS
 # ifdef HAVE_MONOTONIC_CLOCK
 #  define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_MONOTONIC
@@ -145,7 +143,7 @@ static GMutex _gst_sysclock_mutex;
 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
 
 #define gst_system_clock_parent_class parent_class
-G_DEFINE_TYPE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK);
+G_DEFINE_TYPE_WITH_PRIVATE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK);
 
 static void
 gst_system_clock_class_init (GstSystemClockClass * klass)
@@ -156,8 +154,6 @@ gst_system_clock_class_init (GstSystemClockClass * klass)
   gobject_class = (GObjectClass *) klass;
   gstclock_class = (GstClockClass *) klass;
 
-  g_type_class_add_private (klass, sizeof (GstSystemClockPrivate));
-
   gobject_class->dispose = gst_system_clock_dispose;
   gobject_class->set_property = gst_system_clock_set_property;
   gobject_class->get_property = gst_system_clock_get_property;
@@ -186,7 +182,7 @@ gst_system_clock_init (GstSystemClock * clock)
       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
 
-  clock->priv = priv = GST_SYSTEM_CLOCK_GET_PRIVATE (clock);
+  clock->priv = priv = gst_system_clock_get_instance_private (clock);
 
   priv->clock_type = DEFAULT_CLOCK_TYPE;
   priv->timer = gst_poll_new_timer ();
@@ -200,6 +196,7 @@ gst_system_clock_init (GstSystemClock * clock)
   if (priv->frequency.QuadPart != 0)
     /* we take a base time so that time starts from 0 to ease debugging */
     QueryPerformanceCounter (&priv->start);
+  priv->ratio = GST_SECOND / priv->frequency.QuadPart;
 #endif /* G_OS_WIN32 */
 
 #ifdef __APPLE__
@@ -292,7 +289,7 @@ gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
 
 /**
  * gst_system_clock_set_default:
- * @new_clock: a #GstClock
+ * @new_clock: (allow-none): a #GstClock
  *
  * Sets the default system clock that can be obtained with
  * gst_system_clock_obtain().
@@ -354,8 +351,8 @@ gst_system_clock_obtain (void)
     clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
         "name", "GstSystemClock", NULL);
 
-    g_assert (!g_object_is_floating (G_OBJECT (clock)));
-
+    /* Clear floating flag */
+    gst_object_ref_sink (clock);
     _the_system_clock = clock;
     g_mutex_unlock (&_gst_sysclock_mutex);
   } else {
@@ -374,14 +371,18 @@ gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
   g_return_if_fail (sysclock->priv->wakeup_count > 0);
 
   sysclock->priv->wakeup_count--;
-  if (sysclock->priv->wakeup_count == 0) {
-    /* read the control socket byte when we removed the last wakeup count */
-    GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control");
-    while (!gst_poll_read_control (sysclock->priv->timer)) {
-      g_warning ("gstsystemclock: read control failed, trying again\n");
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control");
+  while (!gst_poll_read_control (sysclock->priv->timer)) {
+    if (errno == EWOULDBLOCK) {
+      /* Try again and give other threads the chance to do something */
+      g_thread_yield ();
+      continue;
+    } else {
+      /* Critical error, GstPoll will have printed a critical warning already */
+      break;
     }
-    GST_SYSTEM_CLOCK_BROADCAST (sysclock);
   }
+  GST_SYSTEM_CLOCK_BROADCAST (sysclock);
   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
       sysclock->priv->wakeup_count);
 }
@@ -389,22 +390,8 @@ gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
 static void
 gst_system_clock_add_wakeup (GstSystemClock * sysclock)
 {
-  /* only write the control socket for the first wakeup */
-  if (sysclock->priv->wakeup_count == 0) {
-    GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control");
-    while (!gst_poll_write_control (sysclock->priv->timer)) {
-      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
-        g_warning
-            ("gstsystemclock: write control failed in wakeup_async, trying again: %d:%s\n",
-            errno, g_strerror (errno));
-      } else {
-        g_critical
-            ("gstsystemclock: write control failed in wakeup_async: %d:%s\n",
-            errno, g_strerror (errno));
-        return;
-      }
-    }
-  }
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control");
+  gst_poll_write_control (sysclock->priv->timer);
   sysclock->priv->wakeup_count++;
   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
       sysclock->priv->wakeup_count);
@@ -435,6 +422,7 @@ gst_system_clock_async_thread (GstClock * clock)
 {
   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
   GstSystemClockPrivate *priv = sysclock->priv;
+  GstClockReturn status;
 
   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
   GST_OBJECT_LOCK (clock);
@@ -467,11 +455,32 @@ gst_system_clock_async_thread (GstClock * clock)
 
     /* pick the next entry */
     entry = priv->entries->data;
+
+    /* set entry status to busy before we release the clock lock */
+    do {
+      status = GET_ENTRY_STATUS (entry);
+
+      /* check for unscheduled */
+      if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
+        /* entry was unscheduled, move to the next one */
+        GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
+        goto next_entry;
+      }
+
+      /* for periodic timers, status can be EARLY from a previous run */
+      if (G_UNLIKELY (status != GST_CLOCK_OK && status != GST_CLOCK_EARLY))
+        GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+            status, entry);
+
+      /* mark the entry as busy but watch out for intermediate unscheduled
+       * statuses */
+    } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_BUSY)));
+
     GST_OBJECT_UNLOCK (clock);
 
     requested = entry->time;
 
-    /* now wait for the entry, we already hold the lock */
+    /* now wait for the entry */
     res =
         gst_system_clock_id_wait_jitter_unlocked (clock, (GstClockID) entry,
         NULL, FALSE);
@@ -573,8 +582,8 @@ gst_system_clock_get_internal_time (GstClock * clock)
     /* we prefer the highly accurate performance counters on windows */
     QueryPerformanceCounter (&now);
 
-    return gst_util_uint64_scale (now.QuadPart - sysclock->priv->start.QuadPart,
-        GST_SECOND, sysclock->priv->frequency.QuadPart);
+    return ((now.QuadPart -
+            sysclock->priv->start.QuadPart) * sysclock->priv->ratio);
   } else
 #endif /* G_OS_WIN32 */
 #if !defined HAVE_POSIX_TIMERS || !defined HAVE_CLOCK_GETTIME
@@ -638,6 +647,24 @@ gst_system_clock_get_resolution (GstClock * clock)
 #endif /* __APPLE__ */
 }
 
+static inline void
+gst_system_clock_cleanup_unscheduled (GstSystemClock * sysclock,
+    GstClockEntry * entry)
+{
+  /* try to clean up.
+   * The unschedule function managed to set the status to
+   * unscheduled. We now take the lock and mark the entry as unscheduled.
+   * This makes sure that the unschedule function doesn't perform a
+   * wakeup anymore. If the unschedule function has a change to perform
+   * the wakeup before us, we clean up here */
+  GST_OBJECT_LOCK (sysclock);
+  entry->unscheduled = TRUE;
+  if (entry->woken_up) {
+    gst_system_clock_remove_wakeup (sysclock);
+  }
+  GST_OBJECT_UNLOCK (sysclock);
+}
+
 /* synchronously wait on the given GstClockEntry.
  *
  * We do this by blocking on the global GstPoll timer with
@@ -662,8 +689,10 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
   GstClockReturn status;
 
   status = GET_ENTRY_STATUS (entry);
-  if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
+  if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
+    gst_system_clock_cleanup_unscheduled (sysclock, entry);
     return GST_CLOCK_UNSCHEDULED;
+  }
 
   /* need to call the overridden method because we want to sync against the time
    * of the clock, whatever the subclass uses as a clock. */
@@ -692,19 +721,8 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
     while (TRUE) {
       gint pollret;
 
-      do {
-        status = GET_ENTRY_STATUS (entry);
-
-        /* stop when we are unscheduled */
-        if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
-          goto done;
-
-        /* mark the entry as busy but watch out for intermediate unscheduled
-         * statuses */
-      } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_BUSY)));
-
       /* now wait on the entry, it either times out or the fd is written. The
-       * status of the entry is only BUSY around the poll. */
+       * status of the entry is BUSY only around the poll. */
       pollret = gst_poll_wait (sysclock->priv->timer, diff);
 
       /* get the new status, mark as DONE. We do this so that the unschedule
@@ -715,23 +733,16 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
         /* we were unscheduled, exit immediately */
         if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
           break;
+        if (G_UNLIKELY (status != GST_CLOCK_BUSY))
+          GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+              status, entry);
       } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_DONE)));
 
       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked, status %d, ret %d",
           entry, status, pollret);
 
       if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
-        /* try to clean up The unschedule function managed to set the status to
-         * unscheduled. We now take the lock and mark the entry as unscheduled.
-         * This makes sure that the unschedule function doesn't perform a
-         * wakeup anymore. If the unschedule function has a change to perform
-         * the wakeup before us, we clean up here */
-        GST_OBJECT_LOCK (sysclock);
-        entry->unscheduled = TRUE;
-        if (entry->woken_up) {
-          gst_system_clock_remove_wakeup (sysclock);
-        }
-        GST_OBJECT_UNLOCK (sysclock);
+        gst_system_clock_cleanup_unscheduled (sysclock, entry);
         goto done;
       } else {
         if (G_UNLIKELY (pollret != 0)) {
@@ -764,9 +775,10 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
           /* timeout, this is fine, we can report success now */
           if (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, GST_CLOCK_DONE,
                       GST_CLOCK_OK))) {
-            GST_CAT_DEBUG (GST_CAT_CLOCK, "unexpected status for entry %p",
-                entry);
             status = GET_ENTRY_STATUS (entry);
+            if (status != GST_CLOCK_UNSCHEDULED)
+              GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+                  status, entry);
             goto done;
           } else {
             status = GST_CLOCK_OK;
@@ -789,6 +801,17 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
         } else {
           GST_CAT_DEBUG (GST_CAT_CLOCK,
               "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff);
+          /* we are going to poll again, set status back to busy */
+          do {
+            status = GET_ENTRY_STATUS (entry);
+            /* we were unscheduled, exit immediately */
+            if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
+              goto done;
+            if (G_UNLIKELY (status != GST_CLOCK_DONE))
+              GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+                  status, entry);
+          } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status,
+                      GST_CLOCK_BUSY)));
         }
       }
     }
@@ -796,15 +819,23 @@ gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
     /* we are right on time or too late */
     if (G_UNLIKELY (diff == 0)) {
       if (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_OK))) {
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "unexpected status for entry %p", entry);
         status = GET_ENTRY_STATUS (entry);
+        if (G_LIKELY (status == GST_CLOCK_UNSCHEDULED))
+          gst_system_clock_cleanup_unscheduled (sysclock, entry);
+        else
+          GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+              status, entry);
       } else {
         status = GST_CLOCK_OK;
       }
     } else {
       if (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_EARLY))) {
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "unexpected status for entry %p", entry);
         status = GET_ENTRY_STATUS (entry);
+        if (G_LIKELY (status == GST_CLOCK_UNSCHEDULED))
+          gst_system_clock_cleanup_unscheduled (sysclock, entry);
+        else
+          GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+              status, entry);
       } else {
         status = GST_CLOCK_EARLY;
       }
@@ -818,6 +849,22 @@ static GstClockReturn
 gst_system_clock_id_wait_jitter (GstClock * clock, GstClockEntry * entry,
     GstClockTimeDiff * jitter)
 {
+  GstClockReturn status;
+  do {
+    status = GET_ENTRY_STATUS (entry);
+
+    /* stop when we are unscheduled */
+    if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED))
+      return status;
+
+    if (G_UNLIKELY (status != GST_CLOCK_OK))
+      GST_CAT_ERROR (GST_CAT_CLOCK, "unexpected status %d for entry %p",
+          status, entry);
+
+    /* mark the entry as busy but watch out for intermediate unscheduled
+     * statuses */
+  } while (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_BUSY)));
+
   return gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
 }