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)
105 {
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;
126 }
128 static GType
129 gst_clock_type_get_type (void)
130 {
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;
142 }
144 static void
145 gst_system_clock_class_init (GstSystemClockClass * klass)
146 {
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;
173 }
175 static void
176 gst_system_clock_init (GstSystemClock * clock)
177 {
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
195 }
197 static void
198 gst_system_clock_dispose (GObject * object)
199 {
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 }
232 }
234 static void
235 gst_system_clock_set_property (GObject * object, guint prop_id,
236 const GValue * value, GParamSpec * pspec)
237 {
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 }
250 }
252 static void
253 gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
254 GParamSpec * pspec)
255 {
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 }
266 }
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)
281 {
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;
307 }
309 static void
310 gst_system_clock_remove_wakeup (GstSystemClock * sysclock)
311 {
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);
325 }
327 static void
328 gst_system_clock_add_wakeup (GstSystemClock * sysclock)
329 {
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);
342 }
344 static void
345 gst_system_clock_wait_wakeup (GstSystemClock * sysclock)
346 {
347 while (sysclock->priv->wakeup_count > 0) {
348 GST_CLOCK_WAIT (sysclock);
349 }
350 }
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)
366 {
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");
474 }
476 #ifdef HAVE_POSIX_TIMERS
477 static inline clockid_t
478 clock_type_to_posix_id (GstClockType clock_type)
479 {
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;
486 }
487 #endif
489 /* MT safe */
490 static GstClockTime
491 gst_system_clock_get_internal_time (GstClock * clock)
492 {
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
511 }
513 static guint64
514 gst_system_clock_get_resolution (GstClock * clock)
515 {
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
530 }
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)
551 {
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;
669 }
671 static GstClockReturn
672 gst_system_clock_id_wait_jitter (GstClock * clock, GstClockEntry * entry,
673 GstClockTimeDiff * jitter)
674 {
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 }
692 }
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)
698 {
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;
721 }
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)
732 {
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 }
797 }
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)
808 {
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);
827 }