From 2e1969187da3601d376e85156004290f2673014e Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 3 Jul 2020 06:42:31 +0100 Subject: [PATCH] sul: lws_sul_nonmonotonic_adjust FreeRTOS only supports nonmonotonic time, when we correct it by, eg, ntpclient, we offset all the existing sul timeouts. This adds an internal helper function to correct existing sul timeouts by the step amount, and call it in lws ntpclient implementation when adjusting the gettimeofday() time. --- lib/core-net/private-lib-core-net.h | 3 ++ lib/core-net/sorted-usec-list.c | 68 ++++++++++++++++++++++++++++- lib/system/ntpclient/ntpclient.c | 20 +++++++-- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index a688e03b5..c28a29642 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -1447,6 +1447,9 @@ lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len); int lws_plat_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len); +lws_usec_t +lws_sul_nonmonotonic_adjust(struct lws_context *ctx, int64_t step_us); + enum { LW5CHS_RET_RET0, diff --git a/lib/core-net/sorted-usec-list.c b/lib/core-net/sorted-usec-list.c index 04de59168..895865c56 100644 --- a/lib/core-net/sorted-usec-list.c +++ b/lib/core-net/sorted-usec-list.c @@ -103,7 +103,7 @@ __lws_sul_service_ripe(lws_dll2_owner_t *own, int own_len, lws_usec_t usnow) lws_system_do_attach(pt); /* must be at least 1 */ - assert(own_len); + assert(own_len > 0); /* * Of the own_len sul owning lists, the earliest next sul could be on @@ -154,6 +154,72 @@ __lws_sul_service_ripe(lws_dll2_owner_t *own, int own_len, lws_usec_t usnow) return 0; } +/* + * Normally we use the OS monotonic time, which does not step when the + * gettimeofday() time is adjusted after, eg, ntpclient. But on some OSes, + * high resolution monotonic time doesn't exist; sul time is computed from and + * compared against gettimeofday() time and breaks when that steps. + * + * For those cases, this allows us to retrospectively adjust existing suls on + * all owning lists by the step amount, at the same time we adjust the + * nonmonotonic clock. Then nothing breaks so long as we do this when the + * gettimeofday() clock is stepped. + * + * Linux and so on offer Posix MONOTONIC, which lws uses. FreeRTOS doesn't + * have a high-resolution monotonic clock and has to use gettimeofday(), which + * requires this adjustment when it is stepped. + */ + +lws_usec_t +lws_sul_nonmonotonic_adjust(struct lws_context *ctx, int64_t step_us) +{ + struct lws_context_per_thread *pt = &ctx->pt[0]; + int n, m; + + /* + * for each pt + */ + + for (m = 0; m < ctx->count_threads; m++) { + + /* + * For each owning list... + */ + + lws_pt_lock(pt, __func__); + + for (n = 0; n < LWS_COUNT_PT_SUL_OWNERS; n++) { + + if (!pt->pt_sul_owner[n].count) + continue; + + /* ... and for every existing sul on a list... */ + + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head( + &pt->pt_sul_owner[n])) { + lws_sorted_usec_list_t *sul = lws_container_of( + p, lws_sorted_usec_list_t, list); + + /* + * ... retrospectively step its ripe time by the + * step we will adjust the gettimeofday() clock + * with + */ + + sul->us += step_us; + + } lws_end_foreach_dll(p); + } + + lws_pt_unlock(pt); + + pt++; + } + + return 0; +} + /* * Earliest wakeable event on any pt */ diff --git a/lib/system/ntpclient/ntpclient.c b/lib/system/ntpclient/ntpclient.c index 012c04196..04e2bad6c 100644 --- a/lib/system/ntpclient/ntpclient.c +++ b/lib/system/ntpclient/ntpclient.c @@ -127,6 +127,8 @@ callback_ntpc(struct lws *wsi, enum lws_callback_reasons reason, void *user, lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); uint8_t pkt[LWS_PRE + 48]; + struct timeval t1; + int64_t delta_us; uint64_t ns; switch (reason) { @@ -224,16 +226,28 @@ do_close: ns = lws_ser_ru32be(((uint8_t *)in) + 40) - 2208988800; ns = (ns * 1000000000) + lws_ser_ru32be(((uint8_t *)in) + 44); - lwsl_notice("%s: Unix time: %llu\n", __func__, - (unsigned long long)ns / 1000000000); + /* + * Compute the step + */ -#if defined(LWS_ESP_PLATFORM) + gettimeofday(&t1, NULL); + + delta_us = (ns / 1000) - + ((t1.tv_sec * LWS_US_PER_SEC) + t1.tv_usec); + + lwsl_notice("%s: Unix time: %llu, step: %lldus\n", __func__, + (unsigned long long)ns / 1000000000, + delta_us); + +#if defined(LWS_PLAT_FREERTOS) { struct timeval t; t.tv_sec = (unsigned long long)ns / 1000000000; t.tv_usec = (ns % 1000000000) / 1000; + lws_sul_nonmonotonic_adjust(wsi->context, delta_us); + settimeofday(&t, NULL); } #endif