summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 79991c2)
raw | patch | inline | side by side (parent: 79991c2)
author | Andy Wingo <wingo@pobox.com> | |
Wed, 23 Nov 2005 12:36:00 +0000 (12:36 +0000) | ||
committer | Andy Wingo <wingo@pobox.com> | |
Wed, 23 Nov 2005 12:36:00 +0000 (12:36 +0000) |
Original commit message from CVS:
2005-11-23 Andy Wingo <wingo@pobox.com>
* check/net/gstnetclientclock.c (test_functioning): Adjust to
rate_num/rate_denom change.
* gst/net/gstnetclientclock.c
(gst_net_client_clock_observe_times): Take the SLAVE_LOCK not the
OBJECT_LOCK. Don't call add_observation with the lock.
* gst/gstclock.c (gst_clock_init): Initialize the rate as a
fraction.
(gst_clock_adjust_unlocked): Adjust using uint64_scale and the
rate fraction.
(gst_clock_set_calibration, gst_clock_get_calibration): Change to
deal with rate as a fraction whose numerator and denominator are
GstClockTime values.
(gst_clock_set_master): Only use the OBJECT_LOCK to set the
master; the other fields are protected by the SLAVE_LOCK.
(do_linear_regression): Note that this must be called with the
SLAVE_LOCK.
(gst_clock_add_observation): Take the SLAVE_LOCK, not the
OBJECT_LOCK. Call set_calibration instead of touching the
variables directly.
(gst_clock_set_property, gst_clock_get_property): Protect
master/slave parameters with the SLAVE_LOCK.
* gst/gstclock.h (GstClock): Remove rate, add rate_numerator and
rate_denominator. PR3C1S3. Add a new lock, the SLAVE_LOCK, and
note that all of the instance variables that add_observation and
the set_master functions use are protected by that lock and not
the OBJECT_LOCK.
(GST_CLOCK_SLAVE_LOCK, GST_CLOCK_SLAVE_UNLOCK): New macros.
* gst/gstclock.c (gst_clock_add_observation): No longer requires
the caller to take the object lock.
2005-11-23 Andy Wingo <wingo@pobox.com>
* check/net/gstnetclientclock.c (test_functioning): Adjust to
rate_num/rate_denom change.
* gst/net/gstnetclientclock.c
(gst_net_client_clock_observe_times): Take the SLAVE_LOCK not the
OBJECT_LOCK. Don't call add_observation with the lock.
* gst/gstclock.c (gst_clock_init): Initialize the rate as a
fraction.
(gst_clock_adjust_unlocked): Adjust using uint64_scale and the
rate fraction.
(gst_clock_set_calibration, gst_clock_get_calibration): Change to
deal with rate as a fraction whose numerator and denominator are
GstClockTime values.
(gst_clock_set_master): Only use the OBJECT_LOCK to set the
master; the other fields are protected by the SLAVE_LOCK.
(do_linear_regression): Note that this must be called with the
SLAVE_LOCK.
(gst_clock_add_observation): Take the SLAVE_LOCK, not the
OBJECT_LOCK. Call set_calibration instead of touching the
variables directly.
(gst_clock_set_property, gst_clock_get_property): Protect
master/slave parameters with the SLAVE_LOCK.
* gst/gstclock.h (GstClock): Remove rate, add rate_numerator and
rate_denominator. PR3C1S3. Add a new lock, the SLAVE_LOCK, and
note that all of the instance variables that add_observation and
the set_master functions use are protected by that lock and not
the OBJECT_LOCK.
(GST_CLOCK_SLAVE_LOCK, GST_CLOCK_SLAVE_UNLOCK): New macros.
* gst/gstclock.c (gst_clock_add_observation): No longer requires
the caller to take the object lock.
diff --git a/ChangeLog b/ChangeLog
index 49bf9ee86f4b77908582f8a82ad80f58367ab4f0..b4bd5cf6cded4e93a6e6c8852643131c30b0b0c6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+2005-11-23 Andy Wingo <wingo@pobox.com>
+
+ * check/net/gstnetclientclock.c (test_functioning): Adjust to
+ rate_num/rate_denom change.
+
+ * gst/net/gstnetclientclock.c
+ (gst_net_client_clock_observe_times): Take the SLAVE_LOCK not the
+ OBJECT_LOCK. Don't call add_observation with the lock.
+
+ * gst/gstclock.c (gst_clock_init): Initialize the rate as a
+ fraction.
+ (gst_clock_adjust_unlocked): Adjust using uint64_scale and the
+ rate fraction.
+ (gst_clock_set_calibration, gst_clock_get_calibration): Change to
+ deal with rate as a fraction whose numerator and denominator are
+ GstClockTime values.
+ (gst_clock_set_master): Only use the OBJECT_LOCK to set the
+ master; the other fields are protected by the SLAVE_LOCK.
+ (do_linear_regression): Note that this must be called with the
+ SLAVE_LOCK.
+ (gst_clock_add_observation): Take the SLAVE_LOCK, not the
+ OBJECT_LOCK. Call set_calibration instead of touching the
+ variables directly.
+ (gst_clock_set_property, gst_clock_get_property): Protect
+ master/slave parameters with the SLAVE_LOCK.
+
+ * gst/gstclock.h (GstClock): Remove rate, add rate_numerator and
+ rate_denominator. PR3C1S3. Add a new lock, the SLAVE_LOCK, and
+ note that all of the instance variables that add_observation and
+ the set_master functions use are protected by that lock and not
+ the OBJECT_LOCK.
+ (GST_CLOCK_SLAVE_LOCK, GST_CLOCK_SLAVE_UNLOCK): New macros.
+
+ * gst/gstclock.c (gst_clock_add_observation): No longer requires
+ the caller to take the object lock.
+
2005-11-23 Wim Taymans <wim@fluendo.com>
* gst/gsterror.c: (_gst_core_errors_init):
index e62a01eec248645b3834005259571ce812028974..8ff6bc49e7d37bfab5e3ffe3ef7e31a558164109 100644 (file)
{
GstNetTimeProvider *ntp;
GstClock *client, *server;
- GstClockTime basex, basey;
+ GstClockTime basex, basey, rate_num, rate_denom;
GstClockTime servtime, clienttime;
gint port;
- gdouble rate;
server = gst_system_clock_obtain ();
fail_unless (server != NULL, "failed to get system clock");
/* move the clock ahead 100 seconds */
- gst_clock_get_calibration (server, &basex, &basey, &rate);
+ gst_clock_get_calibration (server, &basex, &basey, &rate_num, &rate_denom);
basey += 100 * GST_SECOND;
- gst_clock_set_calibration (server, basex, basey, rate);
+ gst_clock_set_calibration (server, basex, basey, rate_num, rate_denom);
ntp = gst_net_time_provider_new (server, "127.0.0.1", 0);
fail_unless (ntp != NULL, "failed to create network time provider");
diff --git a/gst/gstclock.c b/gst/gstclock.c
index 81e4aa7942bfbed18cfdc02194143f9ec32cca78..46bf9f9f8634fbd61f1c497fff2e50de87e8aa85 100644 (file)
--- a/gst/gstclock.c
+++ b/gst/gstclock.c
clock->internal_calibration = 0;
clock->external_calibration = 0;
- clock->rate = 1.0;
+ clock->rate_numerator = 1;
+ clock->rate_denominator = 1;
+
+ clock->slave_lock = g_mutex_new ();
clock->filling = TRUE;
clock->window_size = DEFAULT_WINDOW_SIZE;
clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
clock->times = NULL;
GST_OBJECT_UNLOCK (clock);
+ g_mutex_free (clock->slave_lock);
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
* Converts the given @internal clock time to the real time, adjusting for the
* rate and reference time set with gst_clock_set_calibration() and making sure
* that the returned time is increasing. This function should be called with the
- * clock LOCK held and is mainly used by clock subclasses.
+ * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
*
* Returns: the converted time of the clock.
*
{
GstClockTime ret;
- ret = (internal - clock->internal_calibration) * clock->rate;
+ ret = gst_util_uint64_scale (internal - clock->internal_calibration,
+ clock->rate_numerator, clock->rate_denominator);
ret += clock->external_calibration;
/* make sure the time is increasing */
* @clock: a #GstClock to calibrate
* @internal: a reference internal time
* @external: a reference external time
- * @rate: the rate of the clock relative to its internal time
+ * @rate_num: the numerator of the rate of the clock relative to its
+ * internal time
+ * @rate_denom: the denominator of the rate of the clock
*
- * Adjusts the rate and time of @clock. A @rate of 1.0 is the normal speed of
- * the clock. Values bigger than 1.0 make the clock go faster. @rate must be
- * positive.
+ * Adjusts the rate and time of @clock. A rate of 1/1 is the normal speed of
+ * the clock. Values bigger than 1/1 make the clock go faster.
*
* @internal and @external are calibration parameters that arrange that
* gst_clock_get_time() should have been @external at internal time @internal.
* follows:
*
* <programlisting>
- * time = (internal_time - @internal) * @rate + @external
+ * time = (internal_time - @internal) * @rate_num / @rate_denom + @external
* </programlisting>
*
+ * This formula is implemented in gst_clock_adjust_unlocked(). Of course, it
+ * tries to do the integer arithmetic as precisely as possible.
+ *
* Note that gst_clock_get_time() always returns increasing values so when you
* move the clock backwards, gst_clock_get_time() will report the previous value
* until the clock catches up.
*/
void
gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
- external, gdouble rate)
+ external, GstClockTime rate_num, GstClockTime rate_denom)
{
g_return_if_fail (GST_IS_CLOCK (clock));
- g_return_if_fail (rate > 0.0);
+ g_return_if_fail (rate_num > 0);
+ g_return_if_fail (rate_denom > 0);
g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
GST_OBJECT_LOCK (clock);
clock->internal_calibration = internal;
clock->external_calibration = external;
- clock->rate = rate;
+ clock->rate_numerator = rate_num;
+ clock->rate_denominator = rate_denom;
GST_OBJECT_UNLOCK (clock);
}
@@ -787,25 +799,28 @@ gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
* @clock: a #GstClock
* @internal: a location to store the internal time
* @external: a location to store the external time
- * @rate: a location to store the rate
+ * @rate_num: a location to store the rate numerator
+ * @rate_denom: a location to store the rate denominator
*
* Gets the internal rate and reference time of @clock. See
* gst_clock_set_calibration() for more information.
*
- * @internal, @external and @rate can be left NULL if the caller
- * is not interested in the values.
+ * @internal, @external, @rate_num, and @rate_denom can be left NULL if the
+ * caller is not interested in the values.
*
* MT safe.
*/
void
gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
- GstClockTime * external, gdouble * rate)
+ GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
{
g_return_if_fail (GST_IS_CLOCK (clock));
GST_OBJECT_LOCK (clock);
- if (rate)
- *rate = clock->rate;
+ if (rate_num)
+ *rate_num = clock->rate_numerator;
+ if (rate_denom)
+ *rate_denom = clock->rate_denominator;
if (external)
*external = clock->external_calibration;
if (internal)
g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
GST_OBJECT_LOCK (clock);
+
/* we always allow setting the master to NULL */
if (master && !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER))
goto not_supported;
GST_DEBUG_OBJECT (clock, "slaving to master clock %p", master);
gst_object_replace ((GstObject **) & clock->master, (GstObject *) master);
+ GST_OBJECT_UNLOCK (clock);
+
+ GST_CLOCK_SLAVE_LOCK (clock);
+
if (clock->clockid) {
gst_clock_id_unschedule (clock->clockid);
gst_clock_id_unref (clock->clockid);
gst_clock_id_wait_async (clock->clockid,
(GstClockCallback) gst_clock_slave_callback, clock);
}
- GST_OBJECT_UNLOCK (clock);
+
+ GST_CLOCK_SLAVE_UNLOCK (clock);
return TRUE;
return result;
}
-/* http://mathworld.wolfram.com/LeastSquaresFitting.html */
+/* http://mathworld.wolfram.com/LeastSquaresFitting.html
+ * with SLAVE_LOCK
+ */
static gboolean
-do_linear_regression (GstClock * clock, gdouble * m,
- GstClockTime * b, GstClockTime * xbase, gdouble * r_squared)
+do_linear_regression (GstClock * clock, GstClockTime * m_num,
+ GstClockTime * m_denom, GstClockTime * b, GstClockTime * xbase,
+ gdouble * r_squared)
{
GstClockTime *newx, *newy;
GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
xbar = ybar = sxx = syy = sxy = 0;
x = clock->times;
- y = clock->times + 2,
- n = clock->filling ? clock->time_index : clock->window_size;
+ y = clock->times + 2;
+ n = clock->filling ? clock->time_index : clock->window_size;
#ifdef DEBUGGING_ENABLED
DEBUG ("doing regression on:");
- for (i = 0; i < n; i++)
- DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[i], y[i]);
+ for (i = j = 0; i < n; i++, j += 4)
+ DEBUG (" %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, x[j], y[j]);
#endif
xmin = ymin = G_MAXUINT64;
sxy += newx4 * newy4 - xbar4 * ybar4;
}
- *m = ((double) sxy) / sxx;
+ *m_num = sxy;
+ *m_denom = sxx;
*xbase = xmin;
- *b = (ybar + ymin) - (GstClockTime) (xbar * *m);
+ *b = (ybar + ymin) - gst_util_uint64_scale (xbar, *m_num, *m_denom);
*r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);
- DEBUG (" m = %g", *m);
+ DEBUG (" m = %g", ((double) *m_num) / *m_denom);
DEBUG (" b = %" G_GUINT64_FORMAT, *b);
DEBUG (" xbase = %" G_GUINT64_FORMAT, *xbase);
DEBUG (" r2 = %g", *r_squared);
/**
* gst_clock_add_observation
* @clock: a #GstClock
- * @slave: an time on the slave
- * @master: an time on the master
+ * @slave: a time on the slave
+ * @master: a time on the master
* @r_squared: a pointer to hold the result
*
* The time @master of the master clock and the time @slave of the slave
* are available, a linear regression algorithm is run on the
* observations and @clock is recalibrated.
*
- * This function should be called with @clock OBJECT_LOCK.
- *
* Returns: TRUE if enough observations were added to run the
* regression algorithm.
*
gst_clock_add_observation (GstClock * clock, GstClockTime slave,
GstClockTime master, gdouble * r_squared)
{
- GstClockTime b, xbase;
- gdouble m;
+ GstClockTime m_num, m_denom, b, xbase;
g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
g_return_val_if_fail (r_squared != NULL, FALSE);
+ GST_CLOCK_SLAVE_LOCK (clock);
+
clock->times[(4 * clock->time_index)] = slave;
clock->times[(4 * clock->time_index) + 2] = master;
if (clock->filling && clock->time_index < clock->window_threshold)
goto filling;
- do_linear_regression (clock, &m, &b, &xbase, r_squared);
+ do_linear_regression (clock, &m_num, &m_denom, &b, &xbase, r_squared);
+
+ GST_CLOCK_SLAVE_UNLOCK (clock);
GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
- "adjusting clock to m=%g, b=%" G_GINT64_FORMAT " (rsquared=%g)", m, b,
- *r_squared);
+ "adjusting clock to m=%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ", b=%"
+ G_GUINT64_FORMAT " (rsquared=%g)", m_num, m_denom, b, *r_squared);
- clock->internal_calibration = xbase;
- clock->external_calibration = b;
- clock->rate = m;
+ gst_clock_set_calibration (clock, xbase, b, m_num, m_denom);
return TRUE;
filling:
{
+ GST_CLOCK_SLAVE_UNLOCK (clock);
+
return FALSE;
}
}
g_object_notify (object, "stats");
break;
case PROP_WINDOW_SIZE:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
clock->window_size = g_value_get_int (value);
clock->window_threshold =
MIN (clock->window_threshold, clock->window_size);
clock->times =
g_renew (GstClockTime, clock->times, 4 * clock->window_size);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
case PROP_WINDOW_THRESHOLD:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
clock->window_threshold =
MIN (g_value_get_int (value), clock->window_size);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
case PROP_TIMEOUT:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
clock->timeout = g_value_get_uint64 (value);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
GST_OBJECT_UNLOCK (clock);
break;
case PROP_WINDOW_SIZE:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
g_value_set_int (value, clock->window_size);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
case PROP_WINDOW_THRESHOLD:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
g_value_set_int (value, clock->window_threshold);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
case PROP_TIMEOUT:
- GST_OBJECT_LOCK (clock);
+ GST_CLOCK_SLAVE_LOCK (clock);
g_value_set_uint64 (value, clock->timeout);
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/gst/gstclock.h b/gst/gstclock.h
index c8f36dbe8bf85e734877748e7def8ea4a91cf891..e9dd2af2a7bcd8bf2c55b93f7d67807e4e325625 100644 (file)
--- a/gst/gstclock.h
+++ b/gst/gstclock.h
#define GST_CLOCK_GET_CLASS(clock) (G_TYPE_INSTANCE_GET_CLASS ((clock), GST_TYPE_CLOCK, GstClockClass))
#define GST_CLOCK_CAST(clock) ((GstClock*)(clock))
+#define GST_CLOCK_SLAVE_LOCK(clock) g_mutex_lock (GST_CLOCK_CAST (clock)->slave_lock)
+#define GST_CLOCK_SLAVE_UNLOCK(clock) g_mutex_unlock (GST_CLOCK_CAST (clock)->slave_lock)
+
/**
* GstClockTime:
*
struct _GstClock {
GstObject object;
+ GMutex *slave_lock; /* order: SLAVE_LOCK, OBJECT_LOCK */
+
/*< protected >*/ /* with LOCK */
GstClockTime internal_calibration;
GstClockTime external_calibration;
- gdouble rate;
+ GstClockTime rate_numerator;
+ GstClockTime rate_denominator;
GstClockTime last_time;
GList *entries;
GCond *entries_changed;
- /*< private >*/
+ /*< private >*/ /* with LOCK */
GstClockTime resolution;
gboolean stats;
/* for master/slave clocks */
GstClock *master;
+
+ /* with SLAVE_LOCK */
gboolean filling;
gint window_size;
gint window_threshold;
GstClockTime gst_clock_get_time (GstClock *clock);
void gst_clock_set_calibration (GstClock *clock, GstClockTime internal,
- GstClockTime external, gdouble rate);
+ GstClockTime external,
+ GstClockTime rate_num,
+ GstClockTime rate_denom);
void gst_clock_get_calibration (GstClock *clock, GstClockTime *internal,
- GstClockTime *external, gdouble *rate);
+ GstClockTime *external,
+ GstClockTime *rate_num,
+ GstClockTime *rate_denom);
+
/* master/slave clocks */
gboolean gst_clock_set_master (GstClock *clock, GstClock *master);
GstClock* gst_clock_get_master (GstClock *clock);
index faf01ec05b51412f939c50c81b8df6cd80c1197a..f508f94775d4a460609c62bf1a24906cccd2ebde 100644 (file)
clock = GST_CLOCK_CAST (self);
- GST_OBJECT_LOCK (self);
gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared);
+ GST_CLOCK_SLAVE_LOCK (self);
if (clock->filling) {
self->current_timeout = 0;
} else {
(1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND;
self->current_timeout = MIN (self->current_timeout, clock->timeout);
}
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
return;
/* update our internal time so get_time() give something around base_time.
assume that the rate is 1 in the beginning. */
internal = gst_clock_get_internal_time (GST_CLOCK (ret));
- gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1.0);
+ gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1, 1);
{
GstClockTime now = gst_clock_get_time (GST_CLOCK (ret));
index faf01ec05b51412f939c50c81b8df6cd80c1197a..f508f94775d4a460609c62bf1a24906cccd2ebde 100644 (file)
clock = GST_CLOCK_CAST (self);
- GST_OBJECT_LOCK (self);
gst_clock_add_observation (GST_CLOCK (self), local_avg, remote, &r_squared);
+ GST_CLOCK_SLAVE_LOCK (self);
if (clock->filling) {
self->current_timeout = 0;
} else {
(1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND;
self->current_timeout = MIN (self->current_timeout, clock->timeout);
}
- GST_OBJECT_UNLOCK (clock);
+ GST_CLOCK_SLAVE_UNLOCK (clock);
return;
/* update our internal time so get_time() give something around base_time.
assume that the rate is 1 in the beginning. */
internal = gst_clock_get_internal_time (GST_CLOCK (ret));
- gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1.0);
+ gst_clock_set_calibration (GST_CLOCK (ret), internal, base_time, 1, 1);
{
GstClockTime now = gst_clock_get_time (GST_CLOCK (ret));
index e62a01eec248645b3834005259571ce812028974..8ff6bc49e7d37bfab5e3ffe3ef7e31a558164109 100644 (file)
{
GstNetTimeProvider *ntp;
GstClock *client, *server;
- GstClockTime basex, basey;
+ GstClockTime basex, basey, rate_num, rate_denom;
GstClockTime servtime, clienttime;
gint port;
- gdouble rate;
server = gst_system_clock_obtain ();
fail_unless (server != NULL, "failed to get system clock");
/* move the clock ahead 100 seconds */
- gst_clock_get_calibration (server, &basex, &basey, &rate);
+ gst_clock_get_calibration (server, &basex, &basey, &rate_num, &rate_denom);
basey += 100 * GST_SECOND;
- gst_clock_set_calibration (server, basex, basey, rate);
+ gst_clock_set_calibration (server, basex, basey, rate_num, rate_denom);
ntp = gst_net_time_provider_new (server, "127.0.0.1", 0);
fail_unless (ntp != NULL, "failed to create network time provider");