]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - gst/gstsystemclock.c
clock: make UNSCHEDULED checks threadsafe
[glsdk/gstreamer0-10.git] / gst / gstsystemclock.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2004 Wim Taymans <wim@fluendo.com>
4  *
5  * gstsystemclock.c: Default clock, uses the system clock
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
23 /**
24  * SECTION:gstsystemclock
25  * @short_description: Default clock that uses the current system time
26  * @see_also: #GstClock
27  *
28  * The GStreamer core provides a GstSystemClock based on the system time.
29  * Asynchronous callbacks are scheduled from an internal thread.
30  *
31  * Clock implementors are encouraged to subclass this systemclock as it
32  * implements the async notification.
33  *
34  * Subclasses can however override all of the important methods for sync and
35  * async notifications to implement their own callback methods or blocking
36  * wait operations.
37  *
38  * Last reviewed on 2006-03-08 (0.10.4)
39  */
41 #include "gst_private.h"
42 #include "gstinfo.h"
43 #include "gstsystemclock.h"
44 #include "gstpoll.h"
46 #include <errno.h>
48 /* Define this to get some extra debug about jitter from each clock_wait */
49 #undef WAIT_DEBUGGING
51 #define GST_TYPE_CLOCK_TYPE (gst_clock_type_get_type())
53 struct _GstSystemClockPrivate
54 {
55   GstClockType clock_type;
56   GstPoll *timer;
57   gint wakeup_count;            /* the number of entries with a pending wakeup */
58   gboolean async_wakeup;        /* if the wakeup was because of a async list change */
59 };
61 #define GST_SYSTEM_CLOCK_GET_PRIVATE(obj)  \
62    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SYSTEM_CLOCK, \
63         GstSystemClockPrivate))
65 enum
66 {
67   PROP_0,
68   PROP_CLOCK_TYPE,
69   /* FILL ME */
70 };
72 /* the one instance of the systemclock */
73 static GstClock *_the_system_clock = NULL;
75 static void gst_system_clock_class_init (GstSystemClockClass * klass);
76 static void gst_system_clock_init (GstSystemClock * clock);
77 static void gst_system_clock_dispose (GObject * object);
78 static void gst_system_clock_set_property (GObject * object, guint prop_id,
79     const GValue * value, GParamSpec * pspec);
80 static void gst_system_clock_get_property (GObject * object, guint prop_id,
81     GValue * value, GParamSpec * pspec);
83 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
84 static guint64 gst_system_clock_get_resolution (GstClock * clock);
85 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock,
86     GstClockEntry * entry, GstClockTimeDiff * jitter);
87 static GstClockReturn gst_system_clock_id_wait_jitter_unlocked
88     (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter,
89     gboolean restart);
90 static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock,
91     GstClockEntry * entry);
92 static void gst_system_clock_id_unschedule (GstClock * clock,
93     GstClockEntry * entry);
94 static void gst_system_clock_async_thread (GstClock * clock);
95 static gboolean gst_system_clock_start_async (GstSystemClock * clock);
97 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
99 static GstClockClass *parent_class = NULL;
101 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
103 GType
104 gst_system_clock_get_type (void)
106   static GType clock_type = 0;
108   if (G_UNLIKELY (clock_type == 0)) {
109     static const GTypeInfo clock_info = {
110       sizeof (GstSystemClockClass),
111       NULL,
112       NULL,
113       (GClassInitFunc) gst_system_clock_class_init,
114       NULL,
115       NULL,
116       sizeof (GstSystemClock),
117       0,
118       (GInstanceInitFunc) gst_system_clock_init,
119       NULL
120     };
122     clock_type = g_type_register_static (GST_TYPE_CLOCK, "GstSystemClock",
123         &clock_info, 0);
124   }
125   return clock_type;
128 static GType
129 gst_clock_type_get_type (void)
131   static GType clock_type_type = 0;
132   static const GEnumValue clock_types[] = {
133     {GST_CLOCK_TYPE_REALTIME, "GST_CLOCK_TYPE_REALTIME", "realtime"},
134     {GST_CLOCK_TYPE_MONOTONIC, "GST_CLOCK_TYPE_MONOTONIC", "monotonic"},
135     {0, NULL, NULL},
136   };
138   if (G_UNLIKELY (!clock_type_type)) {
139     clock_type_type = g_enum_register_static ("GstClockType", clock_types);
140   }
141   return clock_type_type;
144 static void
145 gst_system_clock_class_init (GstSystemClockClass * klass)
147   GObjectClass *gobject_class;
148   GstObjectClass *gstobject_class;
149   GstClockClass *gstclock_class;
151   gobject_class = (GObjectClass *) klass;
152   gstobject_class = (GstObjectClass *) klass;
153   gstclock_class = (GstClockClass *) klass;
155   parent_class = g_type_class_peek_parent (klass);
157   g_type_class_add_private (klass, sizeof (GstSystemClockPrivate));
159   gobject_class->dispose = gst_system_clock_dispose;
160   gobject_class->set_property = gst_system_clock_set_property;
161   gobject_class->get_property = gst_system_clock_get_property;
163   g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE,
164       g_param_spec_enum ("clock-type", "Clock type",
165           "The type of underlying clock implementation used",
166           GST_TYPE_CLOCK_TYPE, GST_CLOCK_TYPE_REALTIME, G_PARAM_READWRITE));
168   gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
169   gstclock_class->get_resolution = gst_system_clock_get_resolution;
170   gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter;
171   gstclock_class->wait_async = gst_system_clock_id_wait_async;
172   gstclock_class->unschedule = gst_system_clock_id_unschedule;
175 static void
176 gst_system_clock_init (GstSystemClock * clock)
178   GST_OBJECT_FLAG_SET (clock,
179       GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
180       GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
181       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
182       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
184   clock->priv = GST_SYSTEM_CLOCK_GET_PRIVATE (clock);
186   clock->priv->clock_type = GST_CLOCK_TYPE_REALTIME;
187   clock->priv->timer = gst_poll_new_timer ();
189 #if 0
190   /* Uncomment this to start the async clock thread straight away */
191   GST_OBJECT_LOCK (clock);
192   gst_system_clock_start_async (clock);
193   GST_OBJECT_UNLOCK (clock);
194 #endif
197 static void
198 gst_system_clock_dispose (GObject * object)
200   GstClock *clock = (GstClock *) object;
201   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
202   GList *entries;
204   /* else we have to stop the thread */
205   GST_OBJECT_LOCK (clock);
206   sysclock->stopping = TRUE;
207   /* unschedule all entries */
208   for (entries = clock->entries; entries; entries = g_list_next (entries)) {
209     GstClockEntry *entry = (GstClockEntry *) entries->data;
211     GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
212     entry->status = GST_CLOCK_UNSCHEDULED;
213   }
214   g_list_free (clock->entries);
215   clock->entries = NULL;
216   GST_CLOCK_BROADCAST (clock);
217   GST_OBJECT_UNLOCK (clock);
219   if (sysclock->thread)
220     g_thread_join (sysclock->thread);
221   sysclock->thread = NULL;
222   GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
224   gst_poll_free (sysclock->priv->timer);
226   G_OBJECT_CLASS (parent_class)->dispose (object);
228   if (_the_system_clock == clock) {
229     _the_system_clock = NULL;
230     GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock");
231   }
234 static void
235 gst_system_clock_set_property (GObject * object, guint prop_id,
236     const GValue * value, GParamSpec * pspec)
238   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
240   switch (prop_id) {
241     case PROP_CLOCK_TYPE:
242       sysclock->priv->clock_type = g_value_get_enum (value);
243       GST_CAT_DEBUG (GST_CAT_CLOCK, "clock-type set to %d",
244           sysclock->priv->clock_type);
245       break;
246     default:
247       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248       break;
249   }
252 static void
253 gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
254     GParamSpec * pspec)
256   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object);
258   switch (prop_id) {
259     case PROP_CLOCK_TYPE:
260       g_value_set_enum (value, sysclock->priv->clock_type);
261       break;
262     default:
263       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
264       break;
265   }
268 /**
269  * gst_system_clock_obtain:
270  *
271  * Get a handle to the default system clock. The refcount of the
272  * clock will be increased so you need to unref the clock after
273  * usage.
274  *
275  * Returns: the default clock.
276  *
277  * MT safe.
278  */
279 GstClock *
280 gst_system_clock_obtain (void)
282   GstClock *clock;
284   g_static_mutex_lock (&_gst_sysclock_mutex);
285   clock = _the_system_clock;
287   if (clock == NULL) {
288     GST_CAT_DEBUG (GST_CAT_CLOCK, "creating new static system clock");
289     clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
290         "name", "GstSystemClock", NULL);
292     /* we created the global clock; take ownership so
293      * we can hand out instances later */
294     gst_object_ref (clock);
295     gst_object_sink (GST_OBJECT (clock));
297     _the_system_clock = clock;
298     g_static_mutex_unlock (&_gst_sysclock_mutex);
299   } else {
300     g_static_mutex_unlock (&_gst_sysclock_mutex);
301     GST_CAT_DEBUG (GST_CAT_CLOCK, "returning static system clock");
302   }
304   /* we ref it since we are a clock factory. */
305   gst_object_ref (clock);
306   return clock;
309 static void
310 gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
312   g_return_if_fail (sysclock->priv->wakeup_count > 0);
314   sysclock->priv->wakeup_count--;
315   if (sysclock->priv->wakeup_count == 0) {
316     /* read the control socket byte when we removed the last wakeup count */
317     GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control");
318     while (!gst_poll_read_control (sysclock->priv->timer)) {
319       g_warning ("gstsystemclock: read control failed, trying again\n");
320     }
321     GST_CLOCK_BROADCAST (sysclock);
322   }
323   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
324       sysclock->priv->wakeup_count);
327 static void
328 gst_system_clock_add_wakeup (GstSystemClock * sysclock)
330   /* only write the control socket for the first wakeup */
331   if (sysclock->priv->wakeup_count == 0) {
332     GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control");
333     while (!gst_poll_write_control (sysclock->priv->timer)) {
334       g_warning
335           ("gstsystemclock: write control failed in wakeup_async, trying again : %d:%s\n",
336           errno, g_strerror (errno));
337     }
338   }
339   sysclock->priv->wakeup_count++;
340   GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d",
341       sysclock->priv->wakeup_count);
344 static void
345 gst_system_clock_wait_wakeup (GstSystemClock * sysclock)
347   while (sysclock->priv->wakeup_count > 0) {
348     GST_CLOCK_WAIT (sysclock);
349   }
352 /* this thread reads the sorted clock entries from the queue.
353  *
354  * It waits on each of them and fires the callback when the timeout occurs.
355  *
356  * When an entry in the queue was canceled before we wait for it, it is
357  * simply skipped.
358  *
359  * When waiting for an entry, it can become canceled, in that case we don't
360  * call the callback but move to the next item in the queue.
361  *
362  * MT safe.
363  */
364 static void
365 gst_system_clock_async_thread (GstClock * clock)
367   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
369   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
370   GST_OBJECT_LOCK (clock);
371   /* signal spinup */
372   GST_CLOCK_BROADCAST (clock);
373   /* now enter our (almost) infinite loop */
374   while (!sysclock->stopping) {
375     GstClockEntry *entry;
376     GstClockTime requested;
377     GstClockReturn res;
379     /* check if something to be done */
380     while (clock->entries == NULL) {
381       GST_CAT_DEBUG (GST_CAT_CLOCK, "no clock entries, waiting..");
382       /* wait for work to do */
383       GST_CLOCK_WAIT (clock);
384       GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal");
385       /* clock was stopping, exit */
386       if (sysclock->stopping)
387         goto exit;
388     }
390     /* see if we have a pending wakeup because the order of the list
391      * changed. */
392     if (sysclock->priv->async_wakeup) {
393       GST_CAT_DEBUG (GST_CAT_CLOCK, "clear async wakeup");
394       gst_system_clock_remove_wakeup (sysclock);
395       sysclock->priv->async_wakeup = FALSE;
396     }
398     /* pick the next entry */
399     entry = clock->entries->data;
400     /* if it was unscheduled, just move on to the next entry */
401     if (entry->status == GST_CLOCK_UNSCHEDULED) {
402       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
403       goto next_entry;
404     }
406     requested = entry->time;
408     /* now wait for the entry, we already hold the lock */
409     res =
410         gst_system_clock_id_wait_jitter_unlocked (clock, (GstClockID) entry,
411         NULL, FALSE);
413     switch (res) {
414       case GST_CLOCK_UNSCHEDULED:
415         /* entry was unscheduled, move to the next */
416         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
417         goto next_entry;
418       case GST_CLOCK_OK:
419       case GST_CLOCK_EARLY:
420       {
421         /* entry timed out normally, fire the callback and move to the next
422          * entry */
423         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p timed out", entry);
424         if (entry->func) {
425           /* unlock before firing the callback */
426           GST_OBJECT_UNLOCK (clock);
427           entry->func (clock, entry->time, (GstClockID) entry,
428               entry->user_data);
429           GST_OBJECT_LOCK (clock);
430         }
431         if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
432           GST_CAT_DEBUG (GST_CAT_CLOCK, "updating periodic entry %p", entry);
433           /* adjust time now */
434           entry->time = requested + entry->interval;
435           /* and resort the list now */
436           clock->entries =
437               g_list_sort (clock->entries, gst_clock_id_compare_func);
438           /* and restart */
439           continue;
440         } else {
441           GST_CAT_DEBUG (GST_CAT_CLOCK, "moving to next entry");
442           goto next_entry;
443         }
444       }
445       case GST_CLOCK_BUSY:
446         /* somebody unlocked the entry but is was not canceled, This means that
447          * either a new entry was added in front of the queue or some other entry
448          * was canceled. Whatever it is, pick the head entry of the list and
449          * continue waiting. */
450         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
452         /* we set the entry back to the OK state. This is needed so that the
453          * _unschedule() code can see if an entry is currently being waited
454          * on (when its state is BUSY). */
455         entry->status = GST_CLOCK_OK;
456         continue;
457       default:
458         GST_CAT_DEBUG (GST_CAT_CLOCK,
459             "strange result %d waiting for %p, skipping", res, entry);
460         g_warning ("%s: strange result %d waiting for %p, skipping",
461             GST_OBJECT_NAME (clock), res, entry);
462         goto next_entry;
463     }
464   next_entry:
465     /* we remove the current entry and unref it */
466     clock->entries = g_list_remove (clock->entries, entry);
467     gst_clock_id_unref ((GstClockID) entry);
468   }
469 exit:
470   /* signal exit */
471   GST_CLOCK_BROADCAST (clock);
472   GST_OBJECT_UNLOCK (clock);
473   GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
476 #ifdef HAVE_POSIX_TIMERS
477 static inline clockid_t
478 clock_type_to_posix_id (GstClockType clock_type)
480 #ifdef HAVE_MONOTONIC_CLOCK
481   if (clock_type == GST_CLOCK_TYPE_MONOTONIC)
482     return CLOCK_MONOTONIC;
483   else
484 #endif
485     return CLOCK_REALTIME;
487 #endif
489 /* MT safe */
490 static GstClockTime
491 gst_system_clock_get_internal_time (GstClock * clock)
493 #ifdef HAVE_POSIX_TIMERS
494   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
495   clockid_t ptype;
496   struct timespec ts;
498   ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
500   if (G_UNLIKELY (clock_gettime (ptype, &ts)))
501     return GST_CLOCK_TIME_NONE;
503   return GST_TIMESPEC_TO_TIME (ts);
504 #else
505   GTimeVal timeval;
507   g_get_current_time (&timeval);
509   return GST_TIMEVAL_TO_TIME (timeval);
510 #endif
513 static guint64
514 gst_system_clock_get_resolution (GstClock * clock)
516 #ifdef HAVE_POSIX_TIMERS
517   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
518   clockid_t ptype;
519   struct timespec ts;
521   ptype = clock_type_to_posix_id (sysclock->priv->clock_type);
523   if (G_UNLIKELY (clock_getres (ptype, &ts)))
524     return GST_CLOCK_TIME_NONE;
526   return GST_TIMESPEC_TO_TIME (ts);
527 #else
528   return 1 * GST_USECOND;
529 #endif
532 /* synchronously wait on the given GstClockEntry.
533  *
534  * We do this by blocking on the global clock GCond variable with
535  * the requested time as a timeout. This allows us to unblock the
536  * entry by signaling the GCond variable.
537  *
538  * Note that signaling the global GCond unlocks all waiting entries. So
539  * we need to check if an unlocked entry has changed when it unlocks.
540  *
541  * Entries that arrive too late are simply not waited on and a
542  * GST_CLOCK_EARLY result is returned.
543  *
544  * should be called with LOCK held.
545  *
546  * MT safe.
547  */
548 static GstClockReturn
549 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
550     GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart)
552   GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
553   GstClockTime entryt, real, now;
554   GstClockTimeDiff diff;
556   /* need to call the overridden method because we want to sync against the time
557    * of the clock, whatever the subclass uses as a clock. */
558   real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
559   now = gst_clock_adjust_unlocked (clock, real);
561   /* get the time of the entry */
562   entryt = GST_CLOCK_ENTRY_TIME (entry);
564   if (jitter) {
565     *jitter = GST_CLOCK_DIFF (entryt, now);
566   }
567   /* the diff of the entry with the clock is the amount of time we have to
568    * wait */
569   diff = entryt - now;
571   GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
572       " time %" GST_TIME_FORMAT
573       " now %" GST_TIME_FORMAT
574       " real %" GST_TIME_FORMAT
575       " diff (time-now) %" G_GINT64_FORMAT,
576       entry,
577       GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff);
579   if (diff > 0) {
580 #ifdef WAIT_DEBUGGING
581     GstClockTime final;
582 #endif
584     while (entry->status != GST_CLOCK_UNSCHEDULED) {
585       gint pollret;
587       /* mark the entry as busy */
588       entry->status = GST_CLOCK_BUSY;
589       GST_OBJECT_UNLOCK (clock);
591       /* now wait on the entry, it either times out or the fd is written. */
592       pollret = gst_poll_wait (sysclock->priv->timer, diff);
594       /* another thread can read the fd before we get the lock */
595       GST_OBJECT_LOCK (clock);
596       if (entry->status == GST_CLOCK_UNSCHEDULED) {
597         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked", entry);
598         gst_system_clock_remove_wakeup (sysclock);
599       } else {
600         if (pollret != 0) {
601           /* some other id got unlocked */
602           if (!restart) {
603             /* this can happen if the entry got unlocked because of an async
604              * entry was added to the head of the async queue. */
605             GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup waiting for entry %p", entry);
606             break;
607           }
609           /* mark ourselves as EARLY, we release the lock and we could be
610            * unscheduled ourselves but we don't want the unscheduling thread
611            * to write on the control socket (it does that when an entry has a
612            * BUSY status). */
613           entry->status = GST_CLOCK_EARLY;
615           /* wait till all the entries got woken up */
616           gst_system_clock_wait_wakeup (sysclock);
618           /* we released the lock in the wait, recheck our status, we don't need
619            * to remove the wakeup count because we marked the entry as EARLY
620            * before releasing the object lock. */
621           if (entry->status == GST_CLOCK_UNSCHEDULED) {
622             GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p got unscheduled", entry);
623             break;
624           }
626           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p needs to be restarted",
627               entry);
628         } else {
629           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout",
630               entry);
631         }
633         /* reschedule if gst_poll_wait returned early or we have to reschedule after
634          * an unlock*/
635         real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
636         now = gst_clock_adjust_unlocked (clock, real);
637         diff = entryt - now;
639         if (diff <= 0) {
640           /* timeout, this is fine, we can report success now */
641           entry->status = GST_CLOCK_OK;
643           GST_CAT_DEBUG (GST_CAT_CLOCK,
644               "entry %p finished, diff %" G_GINT64_FORMAT, entry, diff);
646 #ifdef WAIT_DEBUGGING
647           final = gst_system_clock_get_internal_time (clock);
648           GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
649               " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
650               " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now,
651               now - entryt,
652               (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND,
653               (final - target),
654               ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
655 #endif
656           break;
657         } else {
658           GST_CAT_DEBUG (GST_CAT_CLOCK,
659               "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff);
660         }
661       }
662     }
663   } else if (diff == 0) {
664     entry->status = GST_CLOCK_OK;
665   } else {
666     entry->status = GST_CLOCK_EARLY;
667   }
668   return entry->status;
671 static GstClockReturn
672 gst_system_clock_id_wait_jitter (GstClock * clock, GstClockEntry * entry,
673     GstClockTimeDiff * jitter)
675   GstClockReturn ret;
677   GST_OBJECT_LOCK (clock);
678   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
679     goto was_unscheduled;
681   ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
682   GST_OBJECT_UNLOCK (clock);
684   return ret;
686   /* ERRORS */
687 was_unscheduled:
688   {
689     GST_OBJECT_UNLOCK (clock);
690     return GST_CLOCK_UNSCHEDULED;
691   }
694 /* Start the async clock thread. Must be called with the object lock
695  * held */
696 static gboolean
697 gst_system_clock_start_async (GstSystemClock * clock)
699   GError *error = NULL;
701   if (G_LIKELY (clock->thread != NULL))
702     return TRUE;                /* Thread already running. Nothing to do */
704   clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
705       clock, TRUE, &error);
706   if (G_UNLIKELY (error))
707     goto no_thread;
709   /* wait for it to spin up */
710   GST_CLOCK_WAIT (clock);
712   return TRUE;
714   /* ERRORS */
715 no_thread:
716   {
717     g_warning ("could not create async clock thread: %s", error->message);
718     g_error_free (error);
719   }
720   return FALSE;
723 /* Add an entry to the list of pending async waits. The entry is inserted
724  * in sorted order. If we inserted the entry at the head of the list, we
725  * need to signal the thread as it might either be waiting on it or waiting
726  * for a new entry.
727  *
728  * MT safe.
729  */
730 static GstClockReturn
731 gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
733   GstSystemClock *sysclock;
734   GstClockEntry *head;
736   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
738   GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
740   GST_OBJECT_LOCK (clock);
741   /* Start the clock async thread if needed */
742   if (G_UNLIKELY (!gst_system_clock_start_async (sysclock)))
743     goto thread_error;
745   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
746     goto was_unscheduled;
748   if (clock->entries)
749     head = clock->entries->data;
750   else
751     head = NULL;
753   /* need to take a ref */
754   gst_clock_id_ref ((GstClockID) entry);
755   /* insert the entry in sorted order */
756   clock->entries = g_list_insert_sorted (clock->entries, entry,
757       gst_clock_id_compare_func);
759   /* only need to send the signal if the entry was added to the
760    * front, else the thread is just waiting for another entry and
761    * will get to this entry automatically. */
762   if (clock->entries->data == entry) {
763     GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head");
764     if (head == NULL) {
765       /* the list was empty before, signal the cond so that the async thread can
766        * start taking a look at the queue */
767       GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal");
768       GST_CLOCK_BROADCAST (clock);
769     } else {
770       if (head->status == GST_CLOCK_BUSY) {
771         /* the async thread was waiting for an entry, unlock the wait so that it
772          * looks at the new head entry instead, we only need to do this once */
773         if (!sysclock->priv->async_wakeup) {
774           GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup async thread");
775           sysclock->priv->async_wakeup = TRUE;
776           gst_system_clock_add_wakeup (sysclock);
777         }
778       }
779     }
780   }
781   GST_OBJECT_UNLOCK (clock);
783   return GST_CLOCK_OK;
785   /* ERRORS */
786 thread_error:
787   {
788     /* Could not start the async clock thread */
789     GST_OBJECT_UNLOCK (clock);
790     return GST_CLOCK_ERROR;
791   }
792 was_unscheduled:
793   {
794     GST_OBJECT_UNLOCK (clock);
795     return GST_CLOCK_UNSCHEDULED;
796   }
799 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
800  * and will signal any thread waiting for entries to recheck their entry.
801  * We cannot really decide if the signal is needed or not because the entry
802  * could be waited on in async or sync mode.
803  *
804  * MT safe.
805  */
806 static void
807 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
809   GstSystemClock *sysclock;
811   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
813   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
815   GST_OBJECT_LOCK (clock);
816   if (entry->status == GST_CLOCK_BUSY) {
817     /* the entry was being busy, wake up all entries so that they recheck their
818      * status. We cannot wake up just one entry because allocating such a
819      * datastructure for each entry would be too heavy and unlocking an entry
820      * is usually done when shutting down or some other exceptional case. */
821     GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was BUSY, doing wakeup");
822     gst_system_clock_add_wakeup (sysclock);
823   }
824   /* when it leaves the poll, it'll detect the unscheduled */
825   entry->status = GST_CLOCK_UNSCHEDULED;
826   GST_OBJECT_UNLOCK (clock);