diff --git a/src/httpc.c b/src/httpc.c index 5d1d06a2d2480cd7314e784940d319ca55b08d77..59cc14b30647560b5722b0c510d4172c6738f7ec 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 fa6e91079c403a28080dc8447396c3c9c9341f8a..f17ae163d87a69f57cdd1857572cf0286817c994 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 0000000000000000000000000000000000000000..0ee899debd07797b8a4445b147ceb31980b2e563 --- /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 0000000000000000000000000000000000000000..732fc8349ba5dbbf577132cb95bfb60a8db79803 --- /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 444f4a1fa5583e105be8ad25fa26226984eef29c..67a9c41d0faad44fb9d5a723d03a2961a2f15772 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 f01557871fdb0716dab3d9b7c3992c407ace50ee..71b31cca9bd6d36fdf5d148330cabde53c48cae5 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 0000000000000000000000000000000000000000..34185b781e9cefc5352130d6ef6fa6d8eea658fd --- /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 0000000000000000000000000000000000000000..da595335e514fe500954ec13c5d3f906dfc6b143 --- /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