From: Willy Tarreau Date: Sun, 8 Sep 2024 18:13:53 +0000 (+0000) Subject: MEDIUM: clock: opportunistically use CLOCK_MONOTONIC for the internal time X-Git-Tag: v3.1-dev8~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4150851ce5be04f170e828d8e045e72a73628994;p=thirdparty%2Fhaproxy.git MEDIUM: clock: opportunistically use CLOCK_MONOTONIC for the internal time We already collect CLOCK_MONOTONIC when it's available when leaving the poller, but it's only used for profiling. The functions that return it set the value to zero when it's not available, so we can use that to detect if it works or not. The idea is that if the monotonic time is non-zero, it is ticking and usable, then we use if for now_ns, otherwise we use the corrected date. We continue to apply the now_offset to the returned value because it helps forcing an early time wrap-around. Proceeding like this presents two benefits: - on systems supporting this, the time is much more robust against time changes - when it works, it saves us from having to go through the time correction code, which is usually cheap, but better avoided anyway. Note that idle time calculation continues to rely on the wall-clock time. --- diff --git a/src/clock.c b/src/clock.c index 75a129bd86..5528ec7def 100644 --- a/src/clock.c +++ b/src/clock.c @@ -32,6 +32,7 @@ ullong start_time_ns; /* the process's start date in volatile ullong global_now_ns; /* common monotonic date between all threads, in ns (wraps every 585 yr) */ volatile uint global_now_ms; /* common monotonic date in milliseconds (may wrap) */ +/* when CLOCK_MONOTONIC is supported, the offset is applied from th_ctx->prev_mono_time instead */ THREAD_ALIGNED(64) static llong now_offset; /* global offset between system time and global time in ns */ THREAD_LOCAL ullong now_ns; /* internal monotonic date derived from real clock, in ns (wraps every 585 yr) */ @@ -203,10 +204,16 @@ void clock_update_local_date(int max_wait, int interrupted) llong date_ns; gettimeofday(&date, NULL); - date_ns = tv_to_ns(&date); - th_ctx->curr_mono_time = now_mono_time(); + date_ns = th_ctx->curr_mono_time; + if (date_ns) { + /* no need to go through complex calculations, we have + * monotonic time. The offset will never change. + */ + goto done; + } + /* compute the minimum and maximum local date we may have reached based * on our past date and the associated timeout. There are three possible * extremities: @@ -220,6 +227,7 @@ void clock_update_local_date(int max_wait, int interrupted) */ _tv_ms_add(&min_deadline, &before_poll, max_wait); _tv_ms_add(&max_deadline, &before_poll, max_wait + 100); + date_ns = tv_to_ns(&date); if (unlikely(__tv_islt(&date, &before_poll) || // big jump backwards (!interrupted && __tv_islt(&date, &min_deadline)) || // small jump backwards @@ -237,6 +245,7 @@ void clock_update_local_date(int max_wait, int interrupted) */ HA_ATOMIC_STORE(&now_offset, now_ns - date_ns); } else { + done: /* The date is still within expectations. Let's apply the * now_offset to the system date. Note: ofs if made of two * independent signed ints. @@ -286,12 +295,15 @@ void clock_update_global_date() (now_ms != old_now_ms && !_HA_ATOMIC_CAS(&global_now_ms, &old_now_ms, now_ms))) && __ha_cpu_relax()); - /* and are now updated to the last value of - * global_now_ns and global_now_ms, which were also monotonically - * updated. We can compute the latest offset, we don't care who writes - * it last, the variations will not break the monotonic property. - */ - HA_ATOMIC_STORE(&now_offset, now_ns - tv_to_ns(&date)); + if (!th_ctx->curr_mono_time) { + /* Only update the offset when monotonic time is not available. + * and are now updated to the last value of + * global_now_ns and global_now_ms, which were also monotonically + * updated. We can compute the latest offset, we don't care who writes + * it last, the variations will not break the monotonic property. + */ + HA_ATOMIC_STORE(&now_offset, now_ns - tv_to_ns(&date)); + } } /* must be called once at boot to initialize some global variables */ @@ -301,7 +313,10 @@ void clock_init_process_date(void) th_ctx->prev_mono_time = th_ctx->curr_mono_time = now_mono_time(); // 0 if not supported gettimeofday(&date, NULL); after_poll = before_poll = date; - now_ns = global_now_ns = tv_to_ns(&date); + global_now_ns = th_ctx->curr_mono_time; + if (!global_now_ns) // CLOCK_MONOTONIC not supported + global_now_ns = tv_to_ns(&date); + now_ns = global_now_ns; global_now_ms = ns_to_ms(now_ns); /* force time to wrap 20s after boot: we first compute the time offset @@ -321,6 +336,10 @@ void clock_init_process_date(void) void clock_adjust_now_offset(void) { + /* Only update the offset when monotonic time is not available. */ + if (th_ctx->curr_mono_time) + return; + HA_ATOMIC_STORE(&now_offset, now_ns - tv_to_ns(&date)); }