]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gstreamer0-10.git/blob - gst/gstsystemclock.c
clock: rework the wakeup of entries.
[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     /* pick the next entry */
391     entry = clock->entries->data;
392     /* if it was unscheduled, just move on to the next entry */
393     if (entry->status == GST_CLOCK_UNSCHEDULED) {
394       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
395       goto next_entry;
396     }
398     requested = entry->time;
400     /* see if we have a pending wakeup because the order of the list
401      * changed. */
402     if (sysclock->priv->async_wakeup) {
403       GST_CAT_DEBUG (GST_CAT_CLOCK, "clear async wakeup", entry);
404       gst_system_clock_remove_wakeup (sysclock);
405       sysclock->priv->async_wakeup = FALSE;
406     }
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 */
619           if (entry->status == GST_CLOCK_UNSCHEDULED) {
620             GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p got unscheduled", entry);
621             break;
622           }
624           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p needs to be restarted",
625               entry);
626         } else {
627           GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout",
628               entry);
629         }
631         /* reschedule if gst_poll_wait returned early or we have to reschedule after
632          * an unlock*/
633         real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
634         now = gst_clock_adjust_unlocked (clock, real);
635         diff = entryt - now;
637         if (diff <= 0) {
638           /* timeout, this is fine, we can report success now */
639           entry->status = GST_CLOCK_OK;
641           GST_CAT_DEBUG (GST_CAT_CLOCK,
642               "entry %p finished, diff %" G_GINT64_FORMAT, entry, diff);
644 #ifdef WAIT_DEBUGGING
645           final = gst_system_clock_get_internal_time (clock);
646           GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
647               " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
648               " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now,
649               now - entryt,
650               (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND,
651               (final - target),
652               ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
653 #endif
654           break;
655         } else {
656           GST_CAT_DEBUG (GST_CAT_CLOCK,
657               "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff);
658         }
659       }
660     }
661   } else if (diff == 0) {
662     entry->status = GST_CLOCK_OK;
663   } else {
664     entry->status = GST_CLOCK_EARLY;
665   }
666   return entry->status;
669 static GstClockReturn
670 gst_system_clock_id_wait_jitter (GstClock * clock, GstClockEntry * entry,
671     GstClockTimeDiff * jitter)
673   GstClockReturn ret;
675   GST_OBJECT_LOCK (clock);
676   ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
677   GST_OBJECT_UNLOCK (clock);
679   return ret;
682 /* Start the async clock thread. Must be called with the object lock
683  * held */
684 static gboolean
685 gst_system_clock_start_async (GstSystemClock * clock)
687   GError *error = NULL;
689   if (clock->thread != NULL)
690     return TRUE;                /* Thread already running. Nothing to do */
692   clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
693       clock, TRUE, &error);
694   if (error)
695     goto no_thread;
697   /* wait for it to spin up */
698   GST_CLOCK_WAIT (clock);
700   return TRUE;
702   /* ERRORS */
703 no_thread:
704   {
705     g_warning ("could not create async clock thread: %s", error->message);
706   }
707   return FALSE;
710 /* Add an entry to the list of pending async waits. The entry is inserted
711  * in sorted order. If we inserted the entry at the head of the list, we
712  * need to signal the thread as it might either be waiting on it or waiting
713  * for a new entry.
714  *
715  * MT safe.
716  */
717 static GstClockReturn
718 gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
720   GstSystemClock *sysclock;
721   GstClockEntry *head;
723   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
725   GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
727   GST_OBJECT_LOCK (clock);
729   /* Start the clock async thread if needed */
730   if (!gst_system_clock_start_async (sysclock))
731     goto thread_error;
733   if (clock->entries)
734     head = clock->entries->data;
735   else
736     head = NULL;
738   /* need to take a ref */
739   gst_clock_id_ref ((GstClockID) entry);
740   /* insert the entry in sorted order */
741   clock->entries = g_list_insert_sorted (clock->entries, entry,
742       gst_clock_id_compare_func);
744   /* only need to send the signal if the entry was added to the
745    * front, else the thread is just waiting for another entry and
746    * will get to this entry automatically. */
747   if (clock->entries->data == entry) {
748     GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head");
749     if (head == NULL) {
750       /* the list was empty before, signal the cond so that the async thread can
751        * start taking a look at the queue */
752       GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal");
753       GST_CLOCK_BROADCAST (clock);
754     } else {
755       if (head->status == GST_CLOCK_BUSY) {
756         /* the async thread was waiting for an entry, unlock the wait so that it
757          * looks at the new head entry instead, we only need to do this once */
758         if (!sysclock->priv->async_wakeup) {
759           GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup async thread");
760           sysclock->priv->async_wakeup = TRUE;
761           gst_system_clock_add_wakeup (sysclock);
762         }
763       }
764     }
765   }
766   GST_OBJECT_UNLOCK (clock);
768   return GST_CLOCK_OK;
770 thread_error:
771   /* Could not start the async clock thread */
772   return GST_CLOCK_ERROR;
775 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
776  * and will signal any thread waiting for entries to recheck their entry.
777  * We cannot really decide if the signal is needed or not because the entry
778  * could be waited on in async or sync mode.
779  *
780  * MT safe.
781  */
782 static void
783 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
785   GstSystemClock *sysclock;
787   sysclock = GST_SYSTEM_CLOCK_CAST (clock);
789   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
791   GST_OBJECT_LOCK (clock);
792   if (entry->status == GST_CLOCK_BUSY) {
793     /* the entry was being busy, wake up all entries so that they recheck their
794      * status. We cannot wake up just one entry because allocating such a
795      * datastructure for each entry would be too heavy and unlocking an entry
796      * is usually done when shutting down or some other exceptional case. */
797     GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was BUSY, doing wakeup");
798     gst_system_clock_add_wakeup (sysclock);
799   }
800   /* when it leaves the poll, it'll detect the unscheduled */
801   entry->status = GST_CLOCK_UNSCHEDULED;
802   GST_OBJECT_UNLOCK (clock);