From c37ff5d18889270770e98afa7e0232df4c19210a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 27 Apr 2021 10:16:01 +0200 Subject: [PATCH 1/2] Add nanosleep and usleep Windows shims This commit adds POSIX nanosleep() and usleep() shim implementation for Windows to help implementors use less #ifdef _WIN32 in the code. --- bin/tools/mdig.c | 15 ----- configure.ac | 2 - lib/dns/tests/dnstest.c | 10 --- lib/isc/tests/doh_test.c | 4 +- lib/isc/tests/isctest.c | 10 --- lib/isc/tests/netmgr_test.c | 14 ++--- lib/isc/tests/quota_test.c | 4 +- lib/isc/win32/include/isc/time.h | 30 ++++----- lib/isc/win32/libisc.def.in | 4 ++ lib/isc/win32/time.c | 104 +++++++++++++++++++++++++++++++ lib/ns/tests/nstest.c | 10 --- 11 files changed, 132 insertions(+), 75 deletions(-) diff --git a/bin/tools/mdig.c b/bin/tools/mdig.c index b35292f51e..a8c47b6197 100644 --- a/bin/tools/mdig.c +++ b/bin/tools/mdig.c @@ -2060,21 +2060,6 @@ parse_args(bool is_batchfile, int argc, char **argv) { } } -#ifdef WIN32 -static void -usleep(unsigned int usec) { - HANDLE timer; - LARGE_INTEGER ft; - - ft.QuadPart = -(10 * (__int64)usec); - - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); -} -#endif - /*% Main processing routine for mdig */ int main(int argc, char *argv[]) { diff --git a/configure.ac b/configure.ac index 2cb914d994..e16e0f7bc1 100644 --- a/configure.ac +++ b/configure.ac @@ -1149,8 +1149,6 @@ AC_COMPILE_IFELSE( # AC_CHECK_FUNCS([if_nametoindex]) -AC_CHECK_FUNCS(nanosleep usleep explicit_bzero) - ISC_ATOMIC_LIBS="" AC_CHECK_HEADERS( [stdatomic.h], diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index 9562846dd2..71f0e0c743 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -340,21 +340,11 @@ dns_test_closezonemgr(void) { */ void dns_test_nap(uint32_t usec) { -#ifdef HAVE_NANOSLEEP struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; nanosleep(&ts, NULL); -#elif HAVE_USLEEP - usleep(usec); -#else /* ifdef HAVE_NANOSLEEP */ - /* - * No fractional-second sleep function is available, so we - * round up to the nearest second and sleep instead - */ - sleep((usec / 1000000) + 1); -#endif /* ifdef HAVE_NANOSLEEP */ } isc_result_t diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index 71dedbad6a..759adc97ab 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -653,7 +653,7 @@ doh_timeout_recovery(void **state) { if (atomic_load(&ctimeouts) == 5) { break; } - usleep(1000); + isc_test_nap(1000); } assert_true(atomic_load(&ctimeouts) == 5); @@ -717,7 +717,7 @@ doh_connect_thread(isc_threadarg_t arg) { * errors, to prevent a thundering herd problem. */ if (atomic_load(&slowdown)) { - usleep(1000 * workers); + isc_test_nap(1000 * workers); atomic_store(&slowdown, false); } connect_send_request( diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c index d86007903b..c9b6b6929f 100644 --- a/lib/isc/tests/isctest.c +++ b/lib/isc/tests/isctest.c @@ -171,19 +171,9 @@ isc_test_end(void) { */ void isc_test_nap(uint32_t usec) { -#ifdef HAVE_NANOSLEEP struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; nanosleep(&ts, NULL); -#elif HAVE_USLEEP - usleep(usec); -#else /* ifdef HAVE_NANOSLEEP */ - /* - * No fractional-second sleep function is available, so we - * round up to the nearest second and sleep instead - */ - sleep((usec / 1000000) + 1); -#endif /* ifdef HAVE_NANOSLEEP */ } diff --git a/lib/isc/tests/netmgr_test.c b/lib/isc/tests/netmgr_test.c index 8f1ae9ece7..f167e7433f 100644 --- a/lib/isc/tests/netmgr_test.c +++ b/lib/isc/tests/netmgr_test.c @@ -137,7 +137,7 @@ static isc_nm_recv_cb_t connect_readcb = NULL; __r = WAIT_REPEATS; \ } \ __o = __l; \ - usleep(T_WAIT); \ + isc_test_nap(T_WAIT); \ } while (__r > 0); \ X(v); \ P(__r); \ @@ -606,7 +606,7 @@ connect_thread(isc_threadarg_t arg) { * start slowing down the connections to prevent the * thundering herd problem. */ - usleep((active - workers) * 1000); + isc_test_nap((active - workers) * 1000); } connect(connect_nm); } @@ -1071,7 +1071,7 @@ udp_half_recv_send(void **state __attribute__((unused))) { assert_null(listen_sock); /* Try to send a little while longer */ - usleep((esends / 2) * 10000); + isc_test_nap((esends / 2) * 10000); isc_nm_closedown(connect_nm); @@ -1519,7 +1519,7 @@ stream_half_recv_send(void **state __attribute__((unused))) { assert_null(listen_sock); /* Try to send a little while longer */ - usleep((esends / 2) * 10000); + isc_test_nap((esends / 2) * 10000); isc_nm_closedown(connect_nm); @@ -1787,7 +1787,7 @@ tcpdns_noresponse(void **state __attribute__((unused))) { NULL, noop_accept_cb, NULL, 0, 0, NULL, &listen_sock); if (result != ISC_R_SUCCESS) { isc_refcount_decrement(&active_cconnects); - usleep(1000); + isc_test_nap(1000); } assert_int_equal(result, ISC_R_SUCCESS); @@ -2073,7 +2073,7 @@ tcpdns_half_recv_send(void **state __attribute__((unused))) { assert_null(listen_sock); /* Try to send a little while longer */ - usleep((esends / 2) * 10000); + isc_test_nap((esends / 2) * 10000); isc_nm_closedown(connect_nm); @@ -2675,7 +2675,7 @@ tlsdns_half_recv_send(void **state __attribute__((unused))) { assert_null(listen_sock); /* Try to send a little while longer */ - usleep((esends / 2) * 10000); + isc_test_nap((esends / 2) * 10000); isc_nm_closedown(connect_nm); diff --git a/lib/isc/tests/quota_test.c b/lib/isc/tests/quota_test.c index 1def0c7456..6a8c09bece 100644 --- a/lib/isc/tests/quota_test.c +++ b/lib/isc/tests/quota_test.c @@ -28,6 +28,8 @@ #include #include +#include "isctest.h" + static void isc_quota_get_set_test(void **state) { UNUSED(state); @@ -256,7 +258,7 @@ isc_thread_t g_threads[10 * 100]; static void * quota_detach(void *quotap) { isc_quota_t *quota = (isc_quota_t *)quotap; - usleep(10000); + isc_test_nap(10000); isc_quota_detach("a); return ((isc_threadresult_t)0); } diff --git a/lib/isc/win32/include/isc/time.h b/lib/isc/win32/include/isc/time.h index c52a04061c..8637c71253 100644 --- a/lib/isc/win32/include/isc/time.h +++ b/lib/isc/win32/include/isc/time.h @@ -25,25 +25,19 @@ *** POSIX Shims ***/ -inline struct tm * -gmtime_r(const time_t *clock, struct tm *result) { - errno_t ret = gmtime_s(result, clock); - if (ret != 0) { - errno = ret; - return (NULL); - } - return (result); -} +struct tm * +gmtime_r(const time_t *clock, struct tm *result); -inline struct tm * -localtime_r(const time_t *clock, struct tm *result) { - errno_t ret = localtime_s(result, clock); - if (ret != 0) { - errno = ret; - return (NULL); - } - return (result); -} +struct tm * +localtime_r(const time_t *clock, struct tm *result); + +int +nanosleep(const struct timespec *req, struct timespec *rem); + +typedef uint32_t useconds_t; + +int +usleep(useconds_t usec); /*** *** Intervals diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 1404a8316c..04faf9de85 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -671,6 +671,10 @@ isc_thread_join isc_thread_setaffinity isc_thread_setconcurrency isc_thread_setname +gmtime_r +localtime_r +nanosleep +usleep isc_time_add isc_time_compare isc_time_formatISO8601 diff --git a/lib/isc/win32/time.c b/lib/isc/win32/time.c index 3cce3536ff..316bc90430 100644 --- a/lib/isc/win32/time.c +++ b/lib/isc/win32/time.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -543,3 +544,106 @@ isc_time_formatshorttimestamp(const isc_time_t *t, char *buf, buf[0] = 0; } } + +/* + * POSIX Shims + */ + +struct tm * +gmtime_r(const time_t *clock, struct tm *result) { + errno_t ret = gmtime_s(result, clock); + if (ret != 0) { + errno = ret; + return (NULL); + } + return (result); +} + +struct tm * +localtime_r(const time_t *clock, struct tm *result) { + errno_t ret = localtime_s(result, clock); + if (ret != 0) { + errno = ret; + return (NULL); + } + return (result); +} + +#define BILLION 1000000000 + +static isc_once_t nsec_ticks_once = ISC_ONCE_INIT; +static double nsec_ticks = 0; + +static void +nsec_ticks_init(void) { + LARGE_INTEGER ticks; + RUNTIME_CHECK(QueryPerformanceFrequency(&ticks) != 0); + nsec_ticks = (double)ticks.QuadPart / 1000000000.0; + RUNTIME_CHECK(nsec_ticks != 0.0); +} + +int +nanosleep(const struct timespec *req, struct timespec *rem) { + int_fast64_t sleep_msec; + uint_fast64_t ticks, until; + LARGE_INTEGER before, after; + + RUNTIME_CHECK(isc_once_do(&nsec_ticks_once, nsec_ticks_init) == + ISC_R_SUCCESS); + + if (req->tv_nsec < 0 || BILLION <= req->tv_nsec) { + errno = EINVAL; + return (-1); + } + + /* Sleep() is not interruptible; there is no remaining delay ever */ + if (rem != NULL) { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + + if (req->tv_sec >= 0) { + /* + * For requested delays of one second or more, 15ms resolution + * granularity is sufficient. + */ + Sleep(req->tv_sec * 1000 + req->tv_nsec / 1000000); + + return (0); + } + + /* Sleep has <-8,8> ms precision, so substract 10 milliseconds */ + sleep_msec = (int64_t)req->tv_nsec / 1000000 - 10; + ticks = req->tv_nsec * nsec_ticks; + + RUNTIME_CHECK(QueryPerformanceCounter(&before) != 0); + + until = before.QuadPart + ticks; + + if (sleep_msec > 0) { + Sleep(sleep_msec); + } + + while (true) { + LARGE_INTEGER after; + RUNTIME_CHECK(QueryPerformanceCounter(&after) != 0); + if (after.QuadPart >= until) { + break; + } + } + +done: + return (0); +} + +int +usleep(useconds_t useconds) { + struct timespec req; + + req.tv_sec = useconds / 1000000; + req.tv_nsec = (useconds * 1000) % 1000000000; + + nanosleep(&req, NULL); + + return (0); +} diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 0f309f01a8..173a5c80e7 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -893,21 +893,11 @@ ns_test_hook_catch_call(void *arg, void *data, isc_result_t *resultp) { */ void ns_test_nap(uint32_t usec) { -#ifdef HAVE_NANOSLEEP struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; nanosleep(&ts, NULL); -#elif HAVE_USLEEP - usleep(usec); -#else /* ifdef HAVE_NANOSLEEP */ - /* - * No fractional-second sleep function is available, so we - * round up to the nearest second and sleep instead - */ - sleep((usec / 1000000) + 1); -#endif /* ifdef HAVE_NANOSLEEP */ } isc_result_t From 37c0d196e3818ca9f9510ffb8fc68f67d89d0a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Mon, 3 May 2021 12:10:03 +0200 Subject: [PATCH 2/2] Use uv_sleep in the netmgr code libuv added uv_sleep(unsigned int msec) to the API since 1.34.0. Use that in the netmgr code and define usleep based shim for libuv << 1.34.0. --- configure.ac | 2 +- lib/isc/netmgr/netmgr.c | 12 ++---------- lib/isc/netmgr/uv-compat.h | 4 ++++ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index e16e0f7bc1..b2599b5aca 100644 --- a/configure.ac +++ b/configure.ac @@ -600,7 +600,7 @@ LIBS="$LIBS $LIBUV_LIBS" # Those functions are only provided in newer versions of libuv, we'll be emulating them # for now -AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import uv_udp_connect uv_translate_sys_error]) +AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import uv_udp_connect uv_translate_sys_error uv_sleep]) AX_RESTORE_FLAGS([libuv]) # libnghttp2 diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 5052bd00f6..0248e2bf30 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -512,11 +512,7 @@ isc_nm_destroy(isc_nm_t **mgr0) { * Wait for the manager to be dereferenced elsewhere. */ while (isc_refcount_current(&mgr->references) > 1 && counter++ < 1000) { -#ifdef WIN32 - _sleep(10); -#else /* ifdef WIN32 */ - usleep(10000); -#endif /* ifdef WIN32 */ + uv_sleep(10); } #ifdef NETMGR_TRACE @@ -531,11 +527,7 @@ isc_nm_destroy(isc_nm_t **mgr0) { * Now just patiently wait */ while (isc_refcount_current(&mgr->references) > 1) { -#ifdef WIN32 - _sleep(10); -#else /* ifdef WIN32 */ - usleep(10000); -#endif /* ifdef WIN32 */ + uv_sleep(10); } /* diff --git a/lib/isc/netmgr/uv-compat.h b/lib/isc/netmgr/uv-compat.h index 960e0aed42..8cd4b160f5 100644 --- a/lib/isc/netmgr/uv-compat.h +++ b/lib/isc/netmgr/uv-compat.h @@ -33,6 +33,10 @@ uv_handle_set_data(uv_handle_t *handle, void *data) { } #endif /* ifndef HAVE_UV_HANDLE_SET_DATA */ +#ifndef HAVE_UV_SLEEP +#define uv_sleep(msec) usleep(msec * 1000) +#endif + #ifdef HAVE_UV_UDP_CONNECT #define isc_uv_udp_connect uv_udp_connect #else