gstreamer_core/gst/gstsystemclock.c
changeset 8 4a7fac7dd34a
parent 0 0e761a78d257
child 30 7e817e7e631c
equal deleted inserted replaced
7:71e347f905f2 8:4a7fac7dd34a
    38  * Last reviewed on 2006-03-08 (0.10.4)
    38  * Last reviewed on 2006-03-08 (0.10.4)
    39  */
    39  */
    40 
    40 
    41 #include "gst_private.h"
    41 #include "gst_private.h"
    42 #include "gstinfo.h"
    42 #include "gstinfo.h"
    43 
       
    44 #include "gstsystemclock.h"
    43 #include "gstsystemclock.h"
       
    44 #include "gstenumtypes.h"
       
    45 #include "gstpoll.h"
       
    46 
       
    47 #include <errno.h>
    45 
    48 
    46 #ifdef __SYMBIAN32__
    49 #ifdef __SYMBIAN32__
    47 #include <glib_global.h>
    50 #include <glib_global.h>
    48 #endif
    51 #endif
    49 
    52 
    50 /* Define this to get some extra debug about jitter from each clock_wait */
    53 /* Define this to get some extra debug about jitter from each clock_wait */
    51 #undef WAIT_DEBUGGING
    54 #undef WAIT_DEBUGGING
       
    55 
       
    56 struct _GstSystemClockPrivate
       
    57 {
       
    58   GstClockType clock_type;
       
    59   GstPoll *timer;
       
    60   gint wakeup_count;            /* the number of entries with a pending wakeup */
       
    61   gboolean async_wakeup;        /* if the wakeup was because of a async list change */
       
    62 };
       
    63 
       
    64 #define GST_SYSTEM_CLOCK_GET_PRIVATE(obj)  \
       
    65    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SYSTEM_CLOCK, \
       
    66         GstSystemClockPrivate))
       
    67 
       
    68 #ifdef HAVE_POSIX_TIMERS
       
    69 # ifdef HAVE_MONOTONIC_CLOCK
       
    70 #  define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_MONOTONIC
       
    71 # else
       
    72 #  define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME
       
    73 # endif
       
    74 #else
       
    75 #define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME
       
    76 #endif
       
    77 
       
    78 enum
       
    79 {
       
    80   PROP_0,
       
    81   PROP_CLOCK_TYPE,
       
    82   /* FILL ME */
       
    83 };
    52 
    84 
    53 /* the one instance of the systemclock */
    85 /* the one instance of the systemclock */
    54 static GstClock *_the_system_clock = NULL;
    86 static GstClock *_the_system_clock = NULL;
    55 
    87 
    56 static void gst_system_clock_class_init (GstSystemClockClass * klass);
    88 static void gst_system_clock_class_init (GstSystemClockClass * klass);
    57 static void gst_system_clock_init (GstSystemClock * clock);
    89 static void gst_system_clock_init (GstSystemClock * clock);
    58 static void gst_system_clock_dispose (GObject * object);
    90 static void gst_system_clock_dispose (GObject * object);
       
    91 static void gst_system_clock_set_property (GObject * object, guint prop_id,
       
    92     const GValue * value, GParamSpec * pspec);
       
    93 static void gst_system_clock_get_property (GObject * object, guint prop_id,
       
    94     GValue * value, GParamSpec * pspec);
    59 
    95 
    60 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
    96 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
    61 static guint64 gst_system_clock_get_resolution (GstClock * clock);
    97 static guint64 gst_system_clock_get_resolution (GstClock * clock);
    62 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock,
    98 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock,
    63     GstClockEntry * entry, GstClockTimeDiff * jitter);
    99     GstClockEntry * entry, GstClockTimeDiff * jitter);
    68     GstClockEntry * entry);
   104     GstClockEntry * entry);
    69 static void gst_system_clock_id_unschedule (GstClock * clock,
   105 static void gst_system_clock_id_unschedule (GstClock * clock,
    70     GstClockEntry * entry);
   106     GstClockEntry * entry);
    71 static void gst_system_clock_async_thread (GstClock * clock);
   107 static void gst_system_clock_async_thread (GstClock * clock);
    72 static gboolean gst_system_clock_start_async (GstSystemClock * clock);
   108 static gboolean gst_system_clock_start_async (GstSystemClock * clock);
       
   109 static void gst_system_clock_add_wakeup (GstSystemClock * sysclock);
    73 
   110 
    74 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
   111 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
    75 
   112 
    76 static GstClockClass *parent_class = NULL;
   113 static GstClockClass *parent_class = NULL;
    77 
   114 
    78 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
   115 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
    79 #ifdef __SYMBIAN32__
   116 
    80 EXPORT_C
   117 G_DEFINE_TYPE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK);
    81 #endif
       
    82 
       
    83 
       
    84 GType
       
    85 gst_system_clock_get_type (void)
       
    86 {
       
    87   static GType clock_type = 0;
       
    88 
       
    89   if (G_UNLIKELY (clock_type == 0)) {
       
    90     static const GTypeInfo clock_info = {
       
    91       sizeof (GstSystemClockClass),
       
    92       NULL,
       
    93       NULL,
       
    94       (GClassInitFunc) gst_system_clock_class_init,
       
    95       NULL,
       
    96       NULL,
       
    97       sizeof (GstSystemClock),
       
    98       0,
       
    99       (GInstanceInitFunc) gst_system_clock_init,
       
   100       NULL
       
   101     };
       
   102 
       
   103     clock_type = g_type_register_static (GST_TYPE_CLOCK, "GstSystemClock",
       
   104         &clock_info, 0);
       
   105   }
       
   106   return clock_type;
       
   107 }
       
   108 
   118 
   109 static void
   119 static void
   110 gst_system_clock_class_init (GstSystemClockClass * klass)
   120 gst_system_clock_class_init (GstSystemClockClass * klass)
   111 {
   121 {
   112   GObjectClass *gobject_class;
   122   GObjectClass *gobject_class;
   113   GstObjectClass *gstobject_class;
       
   114   GstClockClass *gstclock_class;
   123   GstClockClass *gstclock_class;
   115 
   124 
   116   gobject_class = (GObjectClass *) klass;
   125   gobject_class = (GObjectClass *) klass;
   117   gstobject_class = (GstObjectClass *) klass;
       
   118   gstclock_class = (GstClockClass *) klass;
   126   gstclock_class = (GstClockClass *) klass;
   119 
   127 
   120   parent_class = g_type_class_peek_parent (klass);
   128   parent_class = g_type_class_peek_parent (klass);
   121 
   129 
       
   130   g_type_class_add_private (klass, sizeof (GstSystemClockPrivate));
       
   131 
   122   gobject_class->dispose = gst_system_clock_dispose;
   132   gobject_class->dispose = gst_system_clock_dispose;
       
   133   gobject_class->set_property = gst_system_clock_set_property;
       
   134   gobject_class->get_property = gst_system_clock_get_property;
       
   135 
       
   136   g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE,
       
   137       g_param_spec_enum ("clock-type", "Clock type",
       
   138           "The type of underlying clock implementation used",
       
   139           GST_TYPE_CLOCK_TYPE, DEFAULT_CLOCK_TYPE, G_PARAM_READWRITE));
   123 
   140 
   124   gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
   141   gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
   125   gstclock_class->get_resolution = gst_system_clock_get_resolution;
   142   gstclock_class->get_resolution = gst_system_clock_get_resolution;
   126   gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter;
   143   gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter;
   127   gstclock_class->wait_async = gst_system_clock_id_wait_async;
   144   gstclock_class->wait_async = gst_system_clock_id_wait_async;
   135       GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
   152       GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
   136       GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
   153       GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
   137       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
   154       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
   138       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
   155       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
   139 
   156 
       
   157   clock->priv = GST_SYSTEM_CLOCK_GET_PRIVATE (clock);
       
   158 
       
   159   clock->priv->clock_type = DEFAULT_CLOCK_TYPE;
       
   160   clock->priv->timer = gst_poll_new_timer ();
       
   161 
   140 #if 0
   162 #if 0
   141   /* Uncomment this to start the async clock thread straight away */
   163   /* Uncomment this to start the async clock thread straight away */
   142   GST_CLOCK_LOCK (clock);
   164   GST_OBJECT_LOCK (clock);
   143   gst_system_clock_start_async (clock);
   165   gst_system_clock_start_async (clock);
   144   GST_CLOCK_UNLOCK (clock);
   166   GST_OBJECT_UNLOCK (clock);
   145 #endif
   167 #endif
   146 }
   168 }
   147 
   169 
   148 static void
   170 static void
   149 gst_system_clock_dispose (GObject * object)
   171 gst_system_clock_dispose (GObject * object)
   150 {
   172 {
   151   GstClock *clock = (GstClock *) object;
   173   GstClock *clock = (GstClock *) object;
   152 
   174   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
   153   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
       
   154   GList *entries;
   175   GList *entries;
   155 
   176 
   156   /* else we have to stop the thread */
   177   /* else we have to stop the thread */
   157   GST_OBJECT_LOCK (clock);
   178   GST_OBJECT_LOCK (clock);
   158   sysclock->stopping = TRUE;
   179   sysclock->stopping = TRUE;
   164     entry->status = GST_CLOCK_UNSCHEDULED;
   185     entry->status = GST_CLOCK_UNSCHEDULED;
   165   }
   186   }
   166   g_list_free (clock->entries);
   187   g_list_free (clock->entries);
   167   clock->entries = NULL;
   188   clock->entries = NULL;
   168   GST_CLOCK_BROADCAST (clock);
   189   GST_CLOCK_BROADCAST (clock);
       
   190   gst_system_clock_add_wakeup (sysclock);
   169   GST_OBJECT_UNLOCK (clock);
   191   GST_OBJECT_UNLOCK (clock);
   170 
   192 
   171   if (sysclock->thread)
   193   if (sysclock->thread)
   172     g_thread_join (sysclock->thread);
   194     g_thread_join (sysclock->thread);
   173   sysclock->thread = NULL;
   195   sysclock->thread = NULL;
   174   GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
   196   GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
   175 
   197 
       
   198   gst_poll_free (sysclock->priv->timer);
       
   199 
   176   G_OBJECT_CLASS (parent_class)->dispose (object);
   200   G_OBJECT_CLASS (parent_class)->dispose (object);
   177 
   201 
   178   if (_the_system_clock == clock) {
   202   if (_the_system_clock == clock) {
   179     _the_system_clock = NULL;
   203     _the_system_clock = NULL;
   180     GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock");
   204     GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock");
       
   205   }
       
   206 }
       
   207 
       
   208 static void
       
   209 gst_system_clock_set_property (GObject * object, guint prop_id,
       
   210     const GValue * value, GParamSpec * pspec)
       
   211 {
       
   212   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
       
   213 
       
   214   switch (prop_id) {
       
   215     case PROP_CLOCK_TYPE:
       
   216       sysclock->priv->clock_type = g_value_get_enum (value);
       
   217       GST_CAT_DEBUG (GST_CAT_CLOCK, "clock-type set to %d",
       
   218           sysclock->priv->clock_type);
       
   219       break;
       
   220     default:
       
   221       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   222       break;
       
   223   }
       
   224 }
       
   225 
       
   226 static void
       
   227 gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
       
   228     GParamSpec * pspec)
       
   229 {
       
   230   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
       
   231 
       
   232   switch (prop_id) {
       
   233     case PROP_CLOCK_TYPE:
       
   234       g_value_set_enum (value, sysclock->priv->clock_type);
       
   235       break;
       
   236     default:
       
   237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   238       break;
   181   }
   239   }
   182 }
   240 }
   183 
   241 
   184 /**
   242 /**
   185  * gst_system_clock_obtain:
   243  * gst_system_clock_obtain:
   209     clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
   267     clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
   210         "name", "GstSystemClock", NULL);
   268         "name", "GstSystemClock", NULL);
   211 
   269 
   212     /* we created the global clock; take ownership so
   270     /* we created the global clock; take ownership so
   213      * we can hand out instances later */
   271      * we can hand out instances later */
   214     gst_object_ref (clock);
   272     gst_object_ref_sink (clock);
   215     gst_object_sink (GST_OBJECT (clock));
       
   216 
   273 
   217     _the_system_clock = clock;
   274     _the_system_clock = clock;
   218     g_static_mutex_unlock (&_gst_sysclock_mutex);
   275     g_static_mutex_unlock (&_gst_sysclock_mutex);
   219   } else {
   276   } else {
   220     g_static_mutex_unlock (&_gst_sysclock_mutex);
   277     g_static_mutex_unlock (&_gst_sysclock_mutex);
   224   /* we ref it since we are a clock factory. */
   281   /* we ref it since we are a clock factory. */
   225   gst_object_ref (clock);
   282   gst_object_ref (clock);
   226   return clock;
   283   return clock;
   227 }
   284 }
   228 
   285 
       
   286 static void
       
   287 gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
       
   288 {
       
   289   g_return_if_fail (sysclock->priv->wakeup_count > 0);
       
   290 
       
   291   sysclock->priv->wakeup_count--;
       
   292   if (sysclock->priv->wakeup_count == 0) {
       
   293     /* read the control socket byte when we removed the last wakeup count */
       
   294     GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control");
       
   295     while (!gst_poll_read_control (sysclock->priv->timer)) {
       
   296       g_warning ("gstsystemclock: read control failed, trying again\n");
       
   297     }
       
   298     GST_CLOCK_BROADCAST (sysclock);
       
   299   }
       
   300   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
       
   301       sysclock->priv->wakeup_count);
       
   302 }
       
   303 
       
   304 static void
       
   305 gst_system_clock_add_wakeup (GstSystemClock * sysclock)
       
   306 {
       
   307   /* only write the control socket for the first wakeup */
       
   308   if (sysclock->priv->wakeup_count == 0) {
       
   309     GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control");
       
   310     while (!gst_poll_write_control (sysclock->priv->timer)) {
       
   311       g_warning
       
   312           ("gstsystemclock: write control failed in wakeup_async, trying again : %d:%s\n",
       
   313           errno, g_strerror (errno));
       
   314     }
       
   315   }
       
   316   sysclock->priv->wakeup_count++;
       
   317   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
       
   318       sysclock->priv->wakeup_count);
       
   319 }
       
   320 
       
   321 static void
       
   322 gst_system_clock_wait_wakeup (GstSystemClock * sysclock)
       
   323 {
       
   324   while (sysclock->priv->wakeup_count > 0) {
       
   325     GST_CLOCK_WAIT (sysclock);
       
   326   }
       
   327 }
       
   328 
   229 /* this thread reads the sorted clock entries from the queue.
   329 /* this thread reads the sorted clock entries from the queue.
   230  *
   330  *
   231  * It waits on each of them and fires the callback when the timeout occurs.
   331  * It waits on each of them and fires the callback when the timeout occurs.
   232  *
   332  *
   233  * When an entry in the queue was canceled before we wait for it, it is
   333  * When an entry in the queue was canceled before we wait for it, it is
   239  * MT safe.
   339  * MT safe.
   240  */
   340  */
   241 static void
   341 static void
   242 gst_system_clock_async_thread (GstClock * clock)
   342 gst_system_clock_async_thread (GstClock * clock)
   243 {
   343 {
   244   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
   344   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
   245 
   345 
   246   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
   346   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
   247   GST_OBJECT_LOCK (clock);
   347   GST_OBJECT_LOCK (clock);
   248   /* signal spinup */
   348   /* signal spinup */
   249   GST_CLOCK_BROADCAST (clock);
   349   GST_CLOCK_BROADCAST (clock);
   262       /* clock was stopping, exit */
   362       /* clock was stopping, exit */
   263       if (sysclock->stopping)
   363       if (sysclock->stopping)
   264         goto exit;
   364         goto exit;
   265     }
   365     }
   266 
   366 
       
   367     /* see if we have a pending wakeup because the order of the list
       
   368      * changed. */
       
   369     if (sysclock->priv->async_wakeup) {
       
   370       GST_CAT_DEBUG (GST_CAT_CLOCK, "clear async wakeup");
       
   371       gst_system_clock_remove_wakeup (sysclock);
       
   372       sysclock->priv->async_wakeup = FALSE;
       
   373     }
       
   374 
   267     /* pick the next entry */
   375     /* pick the next entry */
   268     entry = clock->entries->data;
   376     entry = clock->entries->data;
   269     /* if it was unscheduled, just move on to the next entry */
   377     /* if it was unscheduled, just move on to the next entry */
   270     if (entry->status == GST_CLOCK_UNSCHEDULED) {
   378     if (entry->status == GST_CLOCK_UNSCHEDULED) {
   271       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
   379       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
   287       case GST_CLOCK_OK:
   395       case GST_CLOCK_OK:
   288       case GST_CLOCK_EARLY:
   396       case GST_CLOCK_EARLY:
   289       {
   397       {
   290         /* entry timed out normally, fire the callback and move to the next
   398         /* entry timed out normally, fire the callback and move to the next
   291          * entry */
   399          * entry */
   292         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
   400         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p timed out", entry);
   293         if (entry->func) {
   401         if (entry->func) {
   294           /* unlock before firing the callback */
   402           /* unlock before firing the callback */
   295           GST_OBJECT_UNLOCK (clock);
   403           GST_OBJECT_UNLOCK (clock);
   296           entry->func (clock, entry->time, (GstClockID) entry,
   404           entry->func (clock, entry->time, (GstClockID) entry,
   297               entry->user_data);
   405               entry->user_data);
   298           GST_OBJECT_LOCK (clock);
   406           GST_OBJECT_LOCK (clock);
   299         }
   407         }
   300         if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
   408         if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
       
   409           GST_CAT_DEBUG (GST_CAT_CLOCK, "updating periodic entry %p", entry);
   301           /* adjust time now */
   410           /* adjust time now */
   302           entry->time = requested + entry->interval;
   411           entry->time = requested + entry->interval;
   303           /* and resort the list now */
   412           /* and resort the list now */
   304           clock->entries =
   413           clock->entries =
   305               g_list_sort (clock->entries, gst_clock_id_compare_func);
   414               g_list_sort (clock->entries, gst_clock_id_compare_func);
   306           /* and restart */
   415           /* and restart */
   307           continue;
   416           continue;
   308         } else {
   417         } else {
       
   418           GST_CAT_DEBUG (GST_CAT_CLOCK, "moving to next entry");
   309           goto next_entry;
   419           goto next_entry;
   310         }
   420         }
   311       }
   421       }
   312       case GST_CLOCK_BUSY:
   422       case GST_CLOCK_BUSY:
   313         /* somebody unlocked the entry but is was not canceled, This means that
   423         /* somebody unlocked the entry but is was not canceled, This means that
   314          * either a new entry was added in front of the queue or some other entry
   424          * either a new entry was added in front of the queue or some other entry
   315          * was canceled. Whatever it is, pick the head entry of the list and
   425          * was canceled. Whatever it is, pick the head entry of the list and
   316          * continue waiting. */
   426          * continue waiting. */
   317         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
   427         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
       
   428 
       
   429         /* we set the entry back to the OK state. This is needed so that the
       
   430          * _unschedule() code can see if an entry is currently being waited
       
   431          * on (when its state is BUSY). */
       
   432         entry->status = GST_CLOCK_OK;
   318         continue;
   433         continue;
   319       default:
   434       default:
   320         GST_CAT_DEBUG (GST_CAT_CLOCK,
   435         GST_CAT_DEBUG (GST_CAT_CLOCK,
   321             "strange result %d waiting for %p, skipping", res, entry);
   436             "strange result %d waiting for %p, skipping", res, entry);
   322         g_warning ("%s: strange result %d waiting for %p, skipping",
   437         g_warning ("%s: strange result %d waiting for %p, skipping",
   333   GST_CLOCK_BROADCAST (clock);
   448   GST_CLOCK_BROADCAST (clock);
   334   GST_OBJECT_UNLOCK (clock);
   449   GST_OBJECT_UNLOCK (clock);
   335   GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
   450   GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
   336 }
   451 }
   337 
   452 
       
   453 #ifdef HAVE_POSIX_TIMERS
       
   454 static inline clockid_t
       
   455 clock_type_to_posix_id (GstClockType clock_type)
       
   456 {
       
   457 #ifdef HAVE_MONOTONIC_CLOCK
       
   458   if (clock_type == GST_CLOCK_TYPE_MONOTONIC)
       
   459     return CLOCK_MONOTONIC;
       
   460   else
       
   461 #endif
       
   462     return CLOCK_REALTIME;
       
   463 }
       
   464 #endif
       
   465 
   338 /* MT safe */
   466 /* MT safe */
   339 static GstClockTime
   467 static GstClockTime
   340 gst_system_clock_get_internal_time (GstClock * clock)
   468 gst_system_clock_get_internal_time (GstClock * clock)
   341 {
   469 {
       
   470 #ifdef HAVE_POSIX_TIMERS
       
   471   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
       
   472   clockid_t ptype;
       
   473   struct timespec ts;
       
   474 
       
   475   ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
       
   476 
       
   477   if (G_UNLIKELY (clock_gettime (ptype, &ts)))
       
   478     return GST_CLOCK_TIME_NONE;
       
   479 
       
   480   return GST_TIMESPEC_TO_TIME (ts);
       
   481 #else
   342   GTimeVal timeval;
   482   GTimeVal timeval;
   343 
   483 
   344   g_get_current_time (&timeval);
   484   g_get_current_time (&timeval);
   345 
   485 
   346   return GST_TIMEVAL_TO_TIME (timeval);
   486   return GST_TIMEVAL_TO_TIME (timeval);
       
   487 #endif
   347 }
   488 }
   348 
   489 
   349 static guint64
   490 static guint64
   350 gst_system_clock_get_resolution (GstClock * clock)
   491 gst_system_clock_get_resolution (GstClock * clock)
   351 {
   492 {
       
   493 #ifdef HAVE_POSIX_TIMERS
       
   494   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
       
   495   clockid_t ptype;
       
   496   struct timespec ts;
       
   497 
       
   498   ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
       
   499 
       
   500   if (G_UNLIKELY (clock_getres (ptype, &ts)))
       
   501     return GST_CLOCK_TIME_NONE;
       
   502 
       
   503   return GST_TIMESPEC_TO_TIME (ts);
       
   504 #else
   352   return 1 * GST_USECOND;
   505   return 1 * GST_USECOND;
       
   506 #endif
   353 }
   507 }
   354 
   508 
   355 /* synchronously wait on the given GstClockEntry.
   509 /* synchronously wait on the given GstClockEntry.
   356  *
   510  *
   357  * We do this by blocking on the global clock GCond variable with
   511  * We do this by blocking on the global clock GCond variable with
   370  */
   524  */
   371 static GstClockReturn
   525 static GstClockReturn
   372 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
   526 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
   373     GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart)
   527     GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart)
   374 {
   528 {
   375   GstClockTime entryt, real, now, target;
   529   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
       
   530   GstClockTime entryt, real, now;
   376   GstClockTimeDiff diff;
   531   GstClockTimeDiff diff;
   377 
   532 
   378   /* need to call the overridden method because we want to sync against the time
   533   /* need to call the overridden method because we want to sync against the time
   379    * of the clock, whatever the subclass uses as a clock. */
   534    * of the clock, whatever the subclass uses as a clock. */
   380   real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
   535   real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
       
   536   now = gst_clock_adjust_unlocked (clock, real);
       
   537 
       
   538   /* get the time of the entry */
   381   entryt = GST_CLOCK_ENTRY_TIME (entry);
   539   entryt = GST_CLOCK_ENTRY_TIME (entry);
   382 
   540 
   383   now = gst_clock_adjust_unlocked (clock, real);
       
   384   if (jitter) {
   541   if (jitter) {
   385     *jitter = GST_CLOCK_DIFF (entryt, now);
   542     *jitter = GST_CLOCK_DIFF (entryt, now);
   386   }
   543   }
   387   /* the diff of the entry with the clock is the amount of time we have to
   544   /* the diff of the entry with the clock is the amount of time we have to
   388    * wait */
   545    * wait */
   389   diff = entryt - now;
   546   diff = entryt - now;
   390   /* Our GCond implementation expects an absolute time against the system clock
       
   391    * as a timeout value. We use our internal time to get the system time and add
       
   392    * the expected timeout to it, this gives us the absolute time of the
       
   393    * timeout. */
       
   394   target = gst_system_clock_get_internal_time (clock) + diff;
       
   395 
   547 
   396   GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
   548   GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
   397       " target %" GST_TIME_FORMAT
   549       " time %" GST_TIME_FORMAT
   398       " entry %" GST_TIME_FORMAT
       
   399       " now %" GST_TIME_FORMAT
   550       " now %" GST_TIME_FORMAT
   400       " real %" GST_TIME_FORMAT
   551       " real %" GST_TIME_FORMAT
   401       " diff (entry-now) %" G_GINT64_FORMAT,
   552       " diff (time-now) %" G_GINT64_FORMAT,
   402       entry,
   553       entry,
   403       GST_TIME_ARGS (target),
       
   404       GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff);
   554       GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff);
   405 
   555 
   406   if (diff > 0) {
   556   if (diff > 0) {
   407     GTimeVal tv;
       
   408 
       
   409 #ifdef WAIT_DEBUGGING
   557 #ifdef WAIT_DEBUGGING
   410     GstClockTime result, final;
   558     GstClockTime final;
   411 #endif
   559 #endif
   412 
   560 
   413     GST_TIME_TO_TIMEVAL (target, tv);
   561     while (entry->status != GST_CLOCK_UNSCHEDULED) {
   414 
   562       gint pollret;
   415     while (TRUE) {
   563 
   416       /* now wait on the entry, it either times out or the cond is signaled. */
   564       /* mark the entry as busy */
   417       if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) {
   565       entry->status = GST_CLOCK_BUSY;
   418         /* timeout, this is fine, we can report success now */
   566       GST_OBJECT_UNLOCK (clock);
   419         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry);
   567 
   420         entry->status = GST_CLOCK_OK;
   568       /* now wait on the entry, it either times out or the fd is written. */
       
   569       pollret = gst_poll_wait (sysclock->priv->timer, diff);
       
   570 
       
   571       /* another thread can read the fd before we get the lock */
       
   572       GST_OBJECT_LOCK (clock);
       
   573       if (entry->status == GST_CLOCK_UNSCHEDULED) {
       
   574         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked", entry);
       
   575         gst_system_clock_remove_wakeup (sysclock);
       
   576       } else {
       
   577         if (pollret != 0) {
       
   578           /* some other id got unlocked */
       
   579           if (!restart) {
       
   580             /* this can happen if the entry got unlocked because of an async
       
   581              * entry was added to the head of the async queue. */
       
   582             GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup waiting for entry %p", entry);
       
   583             break;
       
   584           }
       
   585 
       
   586           /* mark ourselves as EARLY, we release the lock and we could be
       
   587            * unscheduled ourselves but we don't want the unscheduling thread
       
   588            * to write on the control socket (it does that when an entry has a
       
   589            * BUSY status). */
       
   590           entry->status = GST_CLOCK_EARLY;
       
   591 
       
   592           /* wait till all the entries got woken up */
       
   593           gst_system_clock_wait_wakeup (sysclock);
       
   594 
       
   595           /* we released the lock in the wait, recheck our status, we don't need
       
   596            * to remove the wakeup count because we marked the entry as EARLY
       
   597            * before releasing the object lock. */
       
   598           if (entry->status == GST_CLOCK_UNSCHEDULED) {
       
   599             GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p got unscheduled", entry);
       
   600             break;
       
   601           }
       
   602 
       
   603           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p needs to be restarted",
       
   604               entry);
       
   605         } else {
       
   606           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout",
       
   607               entry);
       
   608         }
       
   609 
       
   610         /* reschedule if gst_poll_wait returned early or we have to reschedule after
       
   611          * an unlock*/
       
   612         real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
       
   613         now = gst_clock_adjust_unlocked (clock, real);
       
   614         diff = entryt - now;
       
   615 
       
   616         if (diff <= 0) {
       
   617           /* timeout, this is fine, we can report success now */
       
   618           entry->status = GST_CLOCK_OK;
       
   619 
       
   620           GST_CAT_DEBUG (GST_CAT_CLOCK,
       
   621               "entry %p finished, diff %" G_GINT64_FORMAT, entry, diff);
   421 
   622 
   422 #ifdef WAIT_DEBUGGING
   623 #ifdef WAIT_DEBUGGING
   423         real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
   624           final = gst_system_clock_get_internal_time (clock);
   424         result = gst_clock_adjust_unlocked (clock, real);
   625           GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
   425         final = gst_system_clock_get_internal_time (clock);
   626               " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
   426         GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
   627               " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now,
   427             " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
   628               now - entryt,
   428             " %g target-offset %" G_GINT64_FORMAT " %g", entryt, result,
   629               (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND,
   429             result - entryt,
   630               (final - target),
   430             (double) (GstClockTimeDiff) (result - entryt) / GST_SECOND,
   631               ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
   431             (final - target),
   632 #endif
   432             ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
       
   433 #endif
       
   434         break;
       
   435       } else {
       
   436         /* the waiting is interrupted because the GCond was signaled. This can
       
   437          * be because this or some other entry was unscheduled. */
       
   438         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry);
       
   439         /* if the entry is unscheduled, we can stop waiting for it, else we
       
   440          * continue our while loop. */
       
   441         if (entry->status == GST_CLOCK_UNSCHEDULED)
       
   442           break;
   633           break;
   443         /* else restart if we must */
   634         } else {
   444         if (!restart)
   635           GST_CAT_DEBUG (GST_CAT_CLOCK,
   445           break;
   636               "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff);
   446 
   637         }
   447         /* this can happen if the entry got unlocked because of an async entry
       
   448          * was added to the head of the async queue. */
       
   449         GST_CAT_DEBUG (GST_CAT_CLOCK, "continue waiting for entry %p", entry);
       
   450       }
   638       }
   451     }
   639     }
   452   } else if (diff == 0) {
   640   } else if (diff == 0) {
   453     entry->status = GST_CLOCK_OK;
   641     entry->status = GST_CLOCK_OK;
   454   } else {
   642   } else {
   462     GstClockTimeDiff * jitter)
   650     GstClockTimeDiff * jitter)
   463 {
   651 {
   464   GstClockReturn ret;
   652   GstClockReturn ret;
   465 
   653 
   466   GST_OBJECT_LOCK (clock);
   654   GST_OBJECT_LOCK (clock);
       
   655   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
       
   656     goto was_unscheduled;
       
   657 
   467   ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
   658   ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
   468   GST_OBJECT_UNLOCK (clock);
   659   GST_OBJECT_UNLOCK (clock);
   469 
   660 
   470   return ret;
   661   return ret;
       
   662 
       
   663   /* ERRORS */
       
   664 was_unscheduled:
       
   665   {
       
   666     GST_OBJECT_UNLOCK (clock);
       
   667     return GST_CLOCK_UNSCHEDULED;
       
   668   }
   471 }
   669 }
   472 
   670 
   473 /* Start the async clock thread. Must be called with the object lock
   671 /* Start the async clock thread. Must be called with the object lock
   474  * held */
   672  * held */
   475 static gboolean
   673 static gboolean
   476 gst_system_clock_start_async (GstSystemClock * clock)
   674 gst_system_clock_start_async (GstSystemClock * clock)
   477 {
   675 {
   478   GError *error = NULL;
   676   GError *error = NULL;
   479 
   677 
   480   if (clock->thread != NULL)
   678   if (G_LIKELY (clock->thread != NULL))
   481     return TRUE;                /* Thread already running. Nothing to do */
   679     return TRUE;                /* Thread already running. Nothing to do */
   482 
   680 
   483   clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
   681   clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
   484       clock, TRUE, &error);
   682       clock, TRUE, &error);
   485   if (error)
   683   if (G_UNLIKELY (error))
   486     goto no_thread;
   684     goto no_thread;
   487 
   685 
   488   /* wait for it to spin up */
   686   /* wait for it to spin up */
   489   GST_CLOCK_WAIT (clock);
   687   GST_CLOCK_WAIT (clock);
   490 
   688 
   492 
   690 
   493   /* ERRORS */
   691   /* ERRORS */
   494 no_thread:
   692 no_thread:
   495   {
   693   {
   496     g_warning ("could not create async clock thread: %s", error->message);
   694     g_warning ("could not create async clock thread: %s", error->message);
       
   695     g_error_free (error);
   497   }
   696   }
   498   return FALSE;
   697   return FALSE;
   499 }
   698 }
   500 
   699 
   501 /* Add an entry to the list of pending async waits. The entry is inserted
   700 /* Add an entry to the list of pending async waits. The entry is inserted
   506  * MT safe.
   705  * MT safe.
   507  */
   706  */
   508 static GstClockReturn
   707 static GstClockReturn
   509 gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
   708 gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
   510 {
   709 {
       
   710   GstSystemClock *sysclock;
       
   711   GstClockEntry *head;
       
   712 
       
   713   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
       
   714 
   511   GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
   715   GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
   512 
   716 
   513   GST_OBJECT_LOCK (clock);
   717   GST_OBJECT_LOCK (clock);
   514 
       
   515   /* Start the clock async thread if needed */
   718   /* Start the clock async thread if needed */
   516   if (!gst_system_clock_start_async (GST_SYSTEM_CLOCK (clock)))
   719   if (G_UNLIKELY (!gst_system_clock_start_async (sysclock)))
   517     goto thread_error;
   720     goto thread_error;
       
   721 
       
   722   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
       
   723     goto was_unscheduled;
       
   724 
       
   725   if (clock->entries)
       
   726     head = clock->entries->data;
       
   727   else
       
   728     head = NULL;
   518 
   729 
   519   /* need to take a ref */
   730   /* need to take a ref */
   520   gst_clock_id_ref ((GstClockID) entry);
   731   gst_clock_id_ref ((GstClockID) entry);
   521   /* insert the entry in sorted order */
   732   /* insert the entry in sorted order */
   522   clock->entries = g_list_insert_sorted (clock->entries, entry,
   733   clock->entries = g_list_insert_sorted (clock->entries, entry,
   524 
   735 
   525   /* only need to send the signal if the entry was added to the
   736   /* only need to send the signal if the entry was added to the
   526    * front, else the thread is just waiting for another entry and
   737    * front, else the thread is just waiting for another entry and
   527    * will get to this entry automatically. */
   738    * will get to this entry automatically. */
   528   if (clock->entries->data == entry) {
   739   if (clock->entries->data == entry) {
   529     GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head, sending signal");
   740     GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head");
   530     /* this will wake up _all_ entries waiting for the clock because we have
   741     if (head == NULL) {
   531      * only one cond for all entries (makes allocation faster). Entries that
   742       /* the list was empty before, signal the cond so that the async thread can
   532      * have not timed out will have their status set to BUSY and should continue
   743        * start taking a look at the queue */
   533      * to wait. In the case of the async ones, the new head entry should be
   744       GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal");
   534      * taken and waited for. */
   745       GST_CLOCK_BROADCAST (clock);
   535     GST_CLOCK_BROADCAST (clock);
   746     } else {
       
   747       if (head->status == GST_CLOCK_BUSY) {
       
   748         /* the async thread was waiting for an entry, unlock the wait so that it
       
   749          * looks at the new head entry instead, we only need to do this once */
       
   750         if (!sysclock->priv->async_wakeup) {
       
   751           GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup async thread");
       
   752           sysclock->priv->async_wakeup = TRUE;
       
   753           gst_system_clock_add_wakeup (sysclock);
       
   754         }
       
   755       }
       
   756     }
   536   }
   757   }
   537   GST_OBJECT_UNLOCK (clock);
   758   GST_OBJECT_UNLOCK (clock);
   538 
   759 
   539   return GST_CLOCK_OK;
   760   return GST_CLOCK_OK;
   540 
   761 
       
   762   /* ERRORS */
   541 thread_error:
   763 thread_error:
   542   /* Could not start the async clock thread */
   764   {
   543   return GST_CLOCK_ERROR;
   765     /* Could not start the async clock thread */
       
   766     GST_OBJECT_UNLOCK (clock);
       
   767     return GST_CLOCK_ERROR;
       
   768   }
       
   769 was_unscheduled:
       
   770   {
       
   771     GST_OBJECT_UNLOCK (clock);
       
   772     return GST_CLOCK_UNSCHEDULED;
       
   773   }
   544 }
   774 }
   545 
   775 
   546 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
   776 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
   547  * and will signal any thread waiting for entries to recheck their entry.
   777  * and will signal any thread waiting for entries to recheck their entry.
   548  * We cannot really decide if the signal is needed or not because the entry
   778  * We cannot really decide if the signal is needed or not because the entry
   551  * MT safe.
   781  * MT safe.
   552  */
   782  */
   553 static void
   783 static void
   554 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
   784 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
   555 {
   785 {
       
   786   GstSystemClock *sysclock;
       
   787 
       
   788   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
       
   789 
   556   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
   790   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
   557 
   791 
   558   GST_OBJECT_LOCK (clock);
   792   GST_OBJECT_LOCK (clock);
   559   /* mark entry as unscheduled, then wake up all entries. The entries that did
   793   if (entry->status == GST_CLOCK_BUSY) {
   560    * not timeout will be woken up but immediatly go to sleep again because their
   794     /* the entry was being busy, wake up all entries so that they recheck their
   561    * status would still be busy. */
   795      * status. We cannot wake up just one entry because allocating such a
       
   796      * datastructure for each entry would be too heavy and unlocking an entry
       
   797      * is usually done when shutting down or some other exceptional case. */
       
   798     GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was BUSY, doing wakeup");
       
   799     gst_system_clock_add_wakeup (sysclock);
       
   800   }
       
   801   /* when it leaves the poll, it'll detect the unscheduled */
   562   entry->status = GST_CLOCK_UNSCHEDULED;
   802   entry->status = GST_CLOCK_UNSCHEDULED;
   563   GST_CAT_DEBUG (GST_CAT_CLOCK, "sending signal");
       
   564   GST_CLOCK_BROADCAST (clock);
       
   565   GST_OBJECT_UNLOCK (clock);
   803   GST_OBJECT_UNLOCK (clock);
   566 }
   804 }