/**
* SECTION:gstsystemclock
+ * @title: GstSystemClock
* @short_description: Default clock that uses the current system time
* @see_also: #GstClock
*
* Subclasses can however override all of the important methods for sync and
* async notifications to implement their own callback methods or blocking
* wait operations.
- *
- * Last reviewed on 2006-03-08 (0.10.4)
*/
#include "gst_private.h"
# endif
#endif /* G_OS_WIN32 */
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
+
#define GET_ENTRY_STATUS(e) ((GstClockReturn) g_atomic_int_get(&GST_CLOCK_ENTRY_STATUS(e)))
#define SET_ENTRY_STATUS(e,val) (g_atomic_int_set(&GST_CLOCK_ENTRY_STATUS(e),(val)))
#define CAS_ENTRY_STATUS(e,old,val) (g_atomic_int_compare_and_exchange(\
LARGE_INTEGER start;
LARGE_INTEGER frequency;
#endif /* G_OS_WIN32 */
+#ifdef __APPLE__
+ struct mach_timebase_info mach_timebase;
+#endif
};
#define GST_SYSTEM_CLOCK_GET_PRIVATE(obj) \
QueryPerformanceCounter (&priv->start);
#endif /* G_OS_WIN32 */
+#ifdef __APPLE__
+ mach_timebase_info (&priv->mach_timebase);
+#endif
+
#if 0
/* Uncomment this to start the async clock thread straight away */
GST_OBJECT_LOCK (clock);
/**
* 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.
+ * gst_system_clock_obtain().
*
* This is mostly used for testing and debugging purposes when you
* want to have control over the time reported by the default system
* clock.
*
* MT safe.
+ *
+ * Since: 1.4
*/
void
gst_system_clock_set_default (GstClock * new_clock)
if (clock == NULL) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "creating new static system clock");
- g_assert (_external_default_clock == FALSE);
+ g_assert (!_external_default_clock);
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 {
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);
}
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);
{
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);
/* 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);
static GstClockTime
gst_system_clock_get_internal_time (GstClock * clock)
{
+#if defined __APPLE__
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ uint64_t mach_t = mach_absolute_time ();
+ return gst_util_uint64_scale (mach_t, sysclock->priv->mach_timebase.numer,
+ sysclock->priv->mach_timebase.denom);
+#else
#ifdef G_OS_WIN32
GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
return GST_TIMESPEC_TO_TIME (ts);
}
#endif
+#endif /* __APPLE__ */
}
static guint64
gst_system_clock_get_resolution (GstClock * clock)
{
+#if defined __APPLE__
+ GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+ return gst_util_uint64_scale (GST_NSECOND,
+ sysclock->priv->mach_timebase.numer, sysclock->priv->mach_timebase.denom);
+#else
#ifdef G_OS_WIN32
GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
return 1 * GST_USECOND;
}
#endif
+#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.
GstClockTimeDiff diff;
GstClockReturn status;
- if (G_UNLIKELY (GET_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED))
+ status = GET_ENTRY_STATUS (entry);
+ 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. */
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
/* 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)) {
if (diff <= 0) {
/* timeout, this is fine, we can report success now */
- status = GST_CLOCK_OK;
- SET_ENTRY_STATUS (entry, status);
+ if (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, GST_CLOCK_DONE,
+ GST_CLOCK_OK))) {
+ 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;
+ }
GST_CAT_DEBUG (GST_CAT_CLOCK,
"entry %p finished, diff %" G_GINT64_FORMAT, entry, diff);
} 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)));
}
}
}
} else {
/* we are right on time or too late */
- if (G_UNLIKELY (diff == 0))
- status = GST_CLOCK_OK;
- else
- status = GST_CLOCK_EARLY;
-
- SET_ENTRY_STATUS (entry, status);
+ if (G_UNLIKELY (diff == 0)) {
+ if (G_UNLIKELY (!CAS_ENTRY_STATUS (entry, status, GST_CLOCK_OK))) {
+ 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))) {
+ 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;
+ }
+ }
}
done:
return status;
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);
}