X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstsystemclock.c;h=73dbfb42439b55740d2bfc3e57796c9fc0056d28;hb=53bf06c08814ff9ac3968d47daf11a395cf26a01;hp=0527af29a78721473d9d63bc45e4c8b2fc452509;hpb=154eefecc93b69c6af8040ceef5f004e595a827f;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c index 0527af2..73dbfb4 100644 --- a/gst/gstsystemclock.c +++ b/gst/gstsystemclock.c @@ -22,6 +22,7 @@ /** * SECTION:gstsystemclock + * @title: GstSystemClock * @short_description: Default clock that uses the current system time * @see_also: #GstClock * @@ -55,6 +56,10 @@ # endif #endif /* G_OS_WIN32 */ +#ifdef __APPLE__ +#include +#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(\ @@ -84,13 +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 @@ -138,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) @@ -149,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; @@ -179,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 (); @@ -193,8 +196,13 @@ 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__ + mach_timebase_info (&priv->mach_timebase); +#endif + #if 0 /* Uncomment this to start the async clock thread straight away */ GST_OBJECT_LOCK (clock); @@ -281,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(). @@ -343,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 { @@ -363,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); } @@ -378,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); @@ -424,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); @@ -456,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); @@ -547,6 +567,12 @@ clock_type_to_posix_id (GstClockType clock_type) 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); @@ -556,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 @@ -582,11 +608,17 @@ gst_system_clock_get_internal_time (GstClock * 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); @@ -612,6 +644,25 @@ gst_system_clock_get_resolution (GstClock * 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. @@ -638,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. */ @@ -668,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 @@ -691,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)) { @@ -740,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; @@ -765,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))); } } } @@ -772,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; } @@ -794,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); }