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