• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
3  *
4  * gstsystemclock.c: Unit test for GstSystemClock
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/check/gstcheck.h>
26 
27 static GMutex af_lock;
28 static GCond af_cond;
29 
30 /* see if the defines make sense */
GST_START_TEST(test_range)31 GST_START_TEST (test_range)
32 {
33   GstClockTime time, time2;
34 
35   time = GST_SECOND;
36   fail_unless (time == G_GUINT64_CONSTANT (1000000000));
37 
38   time2 = time / 1000;
39   fail_unless (time2 == 1000000);
40   fail_unless (time2 == GST_MSECOND);
41   fail_unless (time2 == GST_TIME_AS_USECONDS (time));
42 
43   time2 = time / 1000000;
44   fail_unless (time2 == 1000);
45   fail_unless (time2 == GST_USECOND);
46   fail_unless (time2 == GST_TIME_AS_MSECONDS (time));
47 }
48 
49 GST_END_TEST;
50 
GST_START_TEST(test_signedness)51 GST_START_TEST (test_signedness)
52 {
53   GstClockTime time[] = { 0, 1, G_MAXUINT64 / GST_SECOND };
54   GstClockTimeDiff diff[] =
55       { 0, 1, -1, G_MAXINT64 / GST_SECOND, G_MININT64 / GST_SECOND };
56   guint i;
57 
58   for (i = 0; i < G_N_ELEMENTS (time); i++) {
59     fail_if (time[i] != (time[i] * GST_SECOND / GST_SECOND));
60   }
61   for (i = 0; i < G_N_ELEMENTS (diff); i++) {
62     fail_if (diff[i] != (diff[i] * GST_SECOND / GST_SECOND));
63   }
64 }
65 
66 GST_END_TEST;
67 
GST_START_TEST(test_set_default)68 GST_START_TEST (test_set_default)
69 {
70   GstClock *clock, *static_clock;
71 
72   /* obtain the default system clock, which keeps a static ref and bumps the
73    * refcount before returning */
74   static_clock = gst_system_clock_obtain ();
75   fail_unless (static_clock != NULL, "Could not create default system clock");
76   g_assert_cmpint (GST_OBJECT_REFCOUNT (static_clock), ==, 2);
77 
78   /* set a new default clock to a different instance which should replace the
79    * static clock with this one, and unref the static clock */
80   clock = g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", "TestClock", NULL);
81   gst_object_ref_sink (clock);
82   gst_system_clock_set_default (clock);
83   g_assert_cmpint (GST_OBJECT_REFCOUNT (static_clock), ==, 1);
84   gst_object_unref (static_clock);
85   static_clock = gst_system_clock_obtain ();
86   fail_unless (static_clock == clock);
87   g_assert_cmpint (GST_OBJECT_REFCOUNT (clock), ==, 3);
88   gst_object_unref (static_clock);
89 
90   /* Reset the default clock to the static one */
91   gst_system_clock_set_default (NULL);
92   static_clock = gst_system_clock_obtain ();
93   fail_unless (static_clock != clock);
94   g_assert_cmpint (GST_OBJECT_REFCOUNT (clock), ==, 1);
95   g_assert_cmpint (GST_OBJECT_REFCOUNT (static_clock), ==, 2);
96   gst_object_unref (clock);
97   gst_object_unref (static_clock);
98 }
99 
100 GST_END_TEST;
101 
GST_START_TEST(test_diff)102 GST_START_TEST (test_diff)
103 {
104   GstClockTime time1[] = { 0, (GstClockTime) - 1, 0, 1, 2 * GST_SECOND,
105     (GstClockTime) - GST_SECOND, (GstClockTime) - GST_SECOND
106   };
107   GstClockTime time2[] =
108       { 0, 1, 1, 0, 1 * GST_SECOND, (GstClockTime) - GST_SECOND, GST_SECOND };
109   GstClockTimeDiff d[] = { 0, 2, 1, -1, -GST_SECOND, 0, 2 * GST_SECOND };
110   guint i;
111 
112   for (i = 0; i < G_N_ELEMENTS (d); i++) {
113     fail_if (d[i] != GST_CLOCK_DIFF (time1[i], time2[i]));
114   }
115 }
116 
117 GST_END_TEST;
118 
119 static gboolean
test_async_full_slave_callback(GstClock * master,GstClockTime time,GstClockID id,GstClock * clock)120 test_async_full_slave_callback (GstClock * master, GstClockTime time,
121     GstClockID id, GstClock * clock)
122 {
123   GstClockTime stime, mtime;
124   gdouble r_squared;
125 
126   /* notify the test case that we started */
127   GST_INFO ("callback started");
128   g_mutex_lock (&af_lock);
129   g_cond_signal (&af_cond);
130 
131   /* wait for the test case to unref "clock" and signal */
132   GST_INFO ("waiting for test case to signal");
133   g_cond_wait (&af_cond, &af_lock);
134 
135   stime = gst_clock_get_internal_time (clock);
136   mtime = gst_clock_get_time (master);
137 
138   gst_clock_add_observation (clock, stime, mtime, &r_squared);
139 
140   g_cond_signal (&af_cond);
141   g_mutex_unlock (&af_lock);
142   GST_INFO ("callback finished");
143 
144   return TRUE;
145 }
146 
GST_START_TEST(test_async_full)147 GST_START_TEST (test_async_full)
148 {
149   GstClock *master, *slave;
150   GstClockID *clockid;
151 
152   /* create master and slave */
153   master =
154       g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", "TestClockMaster", NULL);
155   gst_object_ref_sink (master);
156   slave = g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", "TestClockMaster", NULL);
157   gst_object_ref_sink (slave);
158   GST_OBJECT_FLAG_SET (slave, GST_CLOCK_FLAG_CAN_SET_MASTER);
159   g_object_set (slave, "timeout", 50 * GST_MSECOND, NULL);
160 
161   fail_unless (GST_OBJECT_REFCOUNT (master) == 1);
162   fail_unless (GST_OBJECT_REFCOUNT (slave) == 1);
163 
164   /* register a periodic shot on the master to calibrate the slave */
165   g_mutex_lock (&af_lock);
166   clockid = gst_clock_new_periodic_id (master,
167       gst_clock_get_time (master), gst_clock_get_timeout (slave));
168   gst_clock_id_wait_async (clockid,
169       (GstClockCallback) test_async_full_slave_callback,
170       gst_object_ref (slave), (GDestroyNotify) gst_object_unref);
171 
172   /* wait for the shot to be fired and test_async_full_slave_callback to be
173    * called */
174   GST_INFO ("waiting for the slave callback to start");
175   g_cond_wait (&af_cond, &af_lock);
176   GST_INFO ("slave callback running, unreffing slave");
177 
178   /* unref the slave clock while the slave_callback is running. This should be
179    * safe since the master clock now stores a ref to the slave */
180   gst_object_unref (slave);
181 
182   /* unref the clock entry. This should be safe as well since the clock thread
183    * refs the entry before executing it */
184   gst_clock_id_unschedule (clockid);
185   gst_clock_id_unref (clockid);
186 
187   /* signal and wait for the callback to complete */
188   g_cond_signal (&af_cond);
189 
190   GST_INFO ("waiting for callback to finish");
191   g_cond_wait (&af_cond, &af_lock);
192   GST_INFO ("callback finished");
193   g_mutex_unlock (&af_lock);
194 
195   gst_object_unref (master);
196 }
197 
198 GST_END_TEST;
199 
GST_START_TEST(test_resolution)200 GST_START_TEST (test_resolution)
201 {
202   GstClock *clock;
203   GstClockTime now_t, prev_t, resolution;
204   int i;
205 
206   now_t = prev_t = GST_CLOCK_TIME_NONE;
207   clock = gst_system_clock_obtain ();
208   fail_unless (clock != NULL, "Could not create default system clock");
209   resolution = gst_clock_get_resolution (clock);
210   fail_unless (resolution != GST_CLOCK_TIME_NONE);
211 
212   for (i = 0; i < 100000; ++i) {
213     now_t = gst_clock_get_internal_time (clock);
214     fail_unless (now_t != GST_CLOCK_TIME_NONE);
215     if (prev_t != GST_CLOCK_TIME_NONE) {
216       GstClockTime diff;
217       fail_unless (now_t >= prev_t);
218       diff = now_t - prev_t;
219       fail_unless (diff == 0 || diff >= resolution);
220     }
221     prev_t = now_t;
222     g_thread_yield ();
223   }
224   gst_object_unref (clock);
225   clock = NULL;
226 }
227 
228 GST_END_TEST;
229 
230 typedef struct
231 {
232   GThread *thread_wait;
233   GThread *thread_unschedule;
234   GMutex lock;
235   gboolean running;
236   GstClockID id;
237   gboolean unschedule;
238   gint32 time_offset_min;
239   gint32 time_offset_max;
240   gboolean dont_unschedule_positive_offset;
241 } WaitUnscheduleData;
242 
243 static gpointer
single_shot_wait_thread_func(gpointer data)244 single_shot_wait_thread_func (gpointer data)
245 {
246   WaitUnscheduleData *d = data;
247   GstClock *clock = gst_system_clock_obtain ();
248 
249   while (d->running) {
250     GstClockTime now;
251     gint offset;
252     GstClockID id;
253 
254     now = gst_clock_get_time (clock);
255     offset = g_random_int_range (d->time_offset_min, d->time_offset_max);
256 
257     g_mutex_lock (&d->lock);
258     d->unschedule = d->dont_unschedule_positive_offset ? offset < 0 : TRUE;
259     id = d->id =
260         gst_clock_new_single_shot_id (clock, now + (GstClockTime) offset);
261     g_mutex_unlock (&d->lock);
262 
263     fail_unless (id != NULL, "Could not create single shot id");
264 
265     gst_clock_id_wait (id, NULL);
266 
267     g_mutex_lock (&d->lock);
268     gst_clock_id_unref (id);
269     d->id = NULL;
270     g_mutex_unlock (&d->lock);
271   }
272 
273   gst_object_unref (clock);
274 
275   return NULL;
276 }
277 
278 static gpointer
unschedule_thread_func(gpointer data)279 unschedule_thread_func (gpointer data)
280 {
281   WaitUnscheduleData *d = data;
282 
283   while (d->running) {
284     g_mutex_lock (&d->lock);
285     if (d->id && d->unschedule) {
286       g_thread_yield ();
287       gst_clock_id_unschedule (d->id);
288     }
289     g_mutex_unlock (&d->lock);
290     g_thread_yield ();
291   }
292 
293   return NULL;
294 }
295 
GST_START_TEST(test_stress_cleanup_unschedule)296 GST_START_TEST (test_stress_cleanup_unschedule)
297 {
298   WaitUnscheduleData *data;
299   gint i, num;
300 
301   num = g_get_num_processors () * 6;
302   data = g_newa (WaitUnscheduleData, num);
303 
304   for (i = 0; i < num; i++) {
305     WaitUnscheduleData *d = &data[i];
306 
307     /* Don't unschedule waits with positive offsets in order to trigger
308      * gst_system_clock_wait_wakeup() */
309     d->dont_unschedule_positive_offset = TRUE;
310     /* Overweight of negative offsets in order to trigger GST_CLOCK_EARLY more
311      * frequently */
312     d->time_offset_min = -GST_MSECOND;
313     d->time_offset_max = GST_MSECOND / 10;
314 
315     /* Initialize test */
316     d->id = NULL;
317     d->running = TRUE;
318     g_mutex_init (&d->lock);
319     d->thread_wait = g_thread_new ("wait", single_shot_wait_thread_func, d);
320     d->thread_unschedule = g_thread_new ("unschedule", unschedule_thread_func,
321         d);
322   }
323 
324   /* Test duration */
325   g_usleep (G_USEC_PER_SEC);
326 
327   /* Stop and free test data */
328   for (i = 0; i < num; i++) {
329     WaitUnscheduleData *d = &data[i];
330     d->running = FALSE;
331   }
332   for (i = 0; i < num; i++) {
333     WaitUnscheduleData *d = &data[i];
334     g_thread_join (d->thread_wait);
335     g_thread_join (d->thread_unschedule);
336     g_mutex_clear (&d->lock);
337   }
338 }
339 
340 GST_END_TEST;
341 
342 
GST_START_TEST(test_stress_reschedule)343 GST_START_TEST (test_stress_reschedule)
344 {
345   WaitUnscheduleData *data;
346   gint i, num;
347 
348   num = g_get_num_processors () * 6;
349   data = g_newa (WaitUnscheduleData, num);
350 
351   for (i = 0; i < num; i++) {
352     WaitUnscheduleData *d = &data[i];
353 
354     /* Try to unschedule all waits */
355     d->dont_unschedule_positive_offset = FALSE;
356     /* Small positive offsets in order to have both negative and positive
357      * diffs when a reschedule is needed. */
358     d->time_offset_min = 0;
359     d->time_offset_max = GST_MSECOND;
360 
361     d->id = NULL;
362     d->running = TRUE;
363     g_mutex_init (&d->lock);
364     d->thread_wait = g_thread_new ("wait", single_shot_wait_thread_func, d);
365     d->thread_unschedule = g_thread_new ("unschedule", unschedule_thread_func,
366         d);
367   }
368 
369   /* Test duration */
370   g_usleep (G_USEC_PER_SEC);
371 
372   /* Stop and free test data */
373   for (i = 0; i < num; i++) {
374     WaitUnscheduleData *d = &data[i];
375     d->running = FALSE;
376   }
377   for (i = 0; i < num; i++) {
378     WaitUnscheduleData *d = &data[i];
379     g_thread_join (d->thread_wait);
380     g_thread_join (d->thread_unschedule);
381     g_mutex_clear (&d->lock);
382   }
383 }
384 
385 GST_END_TEST;
386 
387 
388 static Suite *
gst_systemclock_suite(void)389 gst_systemclock_suite (void)
390 {
391   Suite *s = suite_create ("GstSystemClock");
392   TCase *tc_chain = tcase_create ("waiting");
393 
394   suite_add_tcase (s, tc_chain);
395   tcase_add_test (tc_chain, test_range);
396   tcase_add_test (tc_chain, test_signedness);
397   tcase_add_test (tc_chain, test_diff);
398   tcase_add_test (tc_chain, test_async_full);
399   tcase_add_test (tc_chain, test_set_default);
400   tcase_add_test (tc_chain, test_resolution);
401   tcase_add_test (tc_chain, test_stress_cleanup_unschedule);
402   tcase_add_test (tc_chain, test_stress_reschedule);
403 
404   return s;
405 }
406 
407 GST_CHECK_MAIN (gst_systemclock);
408