From 37d5ac5ac9bdd4db55300b5b31060077edb9f121 Mon Sep 17 00:00:00 2001 From: Andrey Saranchin <Andrey22102001@gmail.com> Date: Thu, 19 May 2022 14:09:55 +0300 Subject: [PATCH] core: introduce clock_lowres This patch introduces not thread-safe low resolution monotonic clock, based on interval timer. It should be used only by thread that initialized it. Part of #6085 NO_CHANGELOG=internal feature NO_DOC=internal feature --- src/httpc.c | 1 + src/lib/core/CMakeLists.txt | 1 + src/lib/core/clock_lowres.c | 98 +++++++++++++++++++++++++++++++++++ src/lib/core/clock_lowres.h | 58 +++++++++++++++++++++ src/main.cc | 3 ++ test/unit/CMakeLists.txt | 2 + test/unit/clock_lowres.c | 43 +++++++++++++++ test/unit/clock_lowres.result | 2 + 8 files changed, 208 insertions(+) create mode 100644 src/lib/core/clock_lowres.c create mode 100644 src/lib/core/clock_lowres.h create mode 100644 test/unit/clock_lowres.c create mode 100644 test/unit/clock_lowres.result diff --git a/src/httpc.c b/src/httpc.c index 5d1d06a2d2..59cc14b306 100644 --- a/src/httpc.c +++ b/src/httpc.c @@ -169,6 +169,7 @@ httpc_request_new(struct httpc_env *env, const char *method, curl_easy_setopt(req->curl_request.easy, CURLOPT_HEADERFUNCTION, curl_easy_header_cb); curl_easy_setopt(req->curl_request.easy, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(req->curl_request.easy, CURLOPT_NOSIGNAL, 1L); ibuf_create(&req->body, &cord()->slabc, 1); diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index fa6e91079c..f17ae163d8 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -38,6 +38,7 @@ set(core_sources mp_datetime.c mp_interval.c prbuf.c + clock_lowres.c ) if(ENABLE_TUPLE_COMPRESSION) diff --git a/src/lib/core/clock_lowres.c b/src/lib/core/clock_lowres.c new file mode 100644 index 0000000000..0ee899debd --- /dev/null +++ b/src/lib/core/clock_lowres.c @@ -0,0 +1,98 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file. + */ +#include <signal.h> +#include <string.h> +#include <say.h> +#include <pthread.h> +#include "clock.h" +#include "clock_lowres.h" + +#if defined(__APPLE__) || !defined(NDEBUG) +/** + * A thread that initialized this module. Only owner thread + * is allowed to handle SIGALRM and use methods from this module. + */ +static pthread_t owner; + +/** Check if current thread is owner. */ +bool +clock_lowres_thread_is_owner(void) +{ + return pthread_equal(owner, pthread_self()); +} +#endif + +/** Resolution of clock (clock update period). */ +static const struct timeval resolution = { + .tv_sec = 0, + .tv_usec = 10 * 1e3, +}; + +double +clock_lowres_resolution(void) +{ + return (double)resolution.tv_sec + resolution.tv_usec / 1e6; +} + +/** Monotonic time, updated once every resolution of time unit. */ +double clock_lowres_monotonic_clock_value = 0.0; + +/** A tick of clock_lowres, SIGALRM handler. */ +static void +clock_lowres_tick(int signum) +{ +#ifdef __APPLE__ + /** + * We cannot guarantee that the signal is delivered + * only to owner thread on MacOS. So use this workaround. + * https://github.com/tarantool/tarantool/issues/7206 + */ + if (!clock_lowres_thread_is_owner()) { + pthread_kill(owner, signum); + return; + } +#else + (void)signum; + assert(clock_lowres_thread_is_owner()); +#endif + clock_lowres_monotonic_clock_value = clock_monotonic(); +} + +void +clock_lowres_signal_init(void) +{ +#if defined(__APPLE__) || !defined(NDEBUG) + owner = pthread_self(); +#endif + clock_lowres_monotonic_clock_value = clock_monotonic(); + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = clock_lowres_tick; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGALRM, &sa, NULL) == -1) + panic_syserror("cannot set low resolution clock timer signal"); + + struct itimerval timer; + timer.it_interval = resolution; + timer.it_value = resolution; + if (setitimer(ITIMER_REAL, &timer, NULL) == -1) + panic_syserror("cannot set low resolution clock timer"); +} + +void +clock_lowres_signal_reset(void) +{ + struct itimerval timer; + memset(&timer, 0, sizeof(timer)); + if (setitimer(ITIMER_REAL, &timer, NULL) == -1) + say_syserror("cannot reset low resolution clock timer"); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGALRM, &sa, NULL) == -1) + say_syserror("cannot reset low resolution clock timer signal"); +} diff --git a/src/lib/core/clock_lowres.h b/src/lib/core/clock_lowres.h new file mode 100644 index 0000000000..732fc8349b --- /dev/null +++ b/src/lib/core/clock_lowres.h @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file. + */ +#pragma once + +/** + * Monotonic low resolution clock, based on interval timer. + * Not thread-safe! + */ + +#if __cplusplus +extern "C" { +#endif + +#include <assert.h> +#include <stdbool.h> + +/** Low resolution clock accumulator. */ +extern double clock_lowres_monotonic_clock_value; + +#ifndef NDEBUG + +/** + * Check that current thread is the thread + * that initialized this module. + */ +bool +clock_lowres_thread_is_owner(void); + +#endif /* NDEBUG */ + +/** Get resolution of clock_lowres clocks in double. */ +double +clock_lowres_resolution(void); + +/** + * Blazingly fast low resolution monotonic time in seconds. + */ +static inline double +clock_lowres_monotonic(void) +{ + assert(clock_lowres_thread_is_owner()); + return clock_lowres_monotonic_clock_value; +} + +/** Initialize signal handler and interval timer. */ +void +clock_lowres_signal_init(void); + +/** Reset signal handler and interval timer. */ +void +clock_lowres_signal_reset(void); + +#if __cplusplus +} +#endif diff --git a/src/main.cc b/src/main.cc index 444f4a1fa5..67a9c41d0f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -85,6 +85,7 @@ #include "core/crash.h" #include "ssl_cert_paths_discover.h" #include "core/errinj.h" +#include "core/clock_lowres.h" static pid_t master_pid = getpid(); static struct pidfh *pid_file_handle; @@ -234,6 +235,7 @@ signal_reset(void) sigaction(SIGWINCH, &sa, NULL) == -1) say_syserror("sigaction"); + clock_lowres_signal_reset(); crash_signal_reset(); /* Unblock any signals blocked by libev. */ @@ -265,6 +267,7 @@ signal_init(void) if (sigaction(SIGPIPE, &sa, 0) == -1) panic_syserror("sigaction"); + clock_lowres_signal_init(); crash_signal_init(); ev_signal_init(&ev_sigs[0], sig_checkpoint, SIGUSR1); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f01557871f..71b31cca9b 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -98,6 +98,8 @@ add_executable(func_cache.test func_cache.c box_test_utils.c) target_link_libraries(func_cache.test box unit) add_executable(prbuf.test prbuf.c) target_link_libraries(prbuf.test unit core) +add_executable(clock_lowres.test clock_lowres.c core_test_utils.c) +target_link_libraries(clock_lowres.test unit core) if (NOT ENABLE_GCOV) # This test is known to be broken with GCOV diff --git a/test/unit/clock_lowres.c b/test/unit/clock_lowres.c new file mode 100644 index 0000000000..34185b781e --- /dev/null +++ b/test/unit/clock_lowres.c @@ -0,0 +1,43 @@ +#include "unit.h" + +#include <math.h> +#include <unistd.h> +#include "clock_lowres.h" +#include "clock.h" + +/** Test duration in seconds. */ +#define TEST_LEN 1.5 + +int +main(void) +{ + plan(1); + clock_lowres_signal_init(); + + /* Such a large resolution to pass the test in debug or apple build. */ + double resolution = clock_lowres_resolution() * 2; + bool success = true; + double start = clock_monotonic(); + double clock = start; + while (clock < start + TEST_LEN) { + /* + * Use pause before getting time so that the process + * does not use all time and unlikely to be + * rescheduled in the middle of check. + * The process will wake up after receiving a signal: + * the clock receives SIGALRM every resolution seconds. + */ + pause(); + double lowres = clock_lowres_monotonic(); + clock = clock_monotonic(); + if (fabs(clock - lowres) > resolution) { + success = false; + break; + } + } + ok(success, "Check that monotonic lowres clock does not diverge " + "too much from monotonic clock"); + + clock_lowres_signal_reset(); + return check_plan(); +} diff --git a/test/unit/clock_lowres.result b/test/unit/clock_lowres.result new file mode 100644 index 0000000000..da595335e5 --- /dev/null +++ b/test/unit/clock_lowres.result @@ -0,0 +1,2 @@ +1..1 +ok 1 - Check that monotonic lowres clock does not diverge too much from monotonic clock -- GitLab