From b71dde8bd94985f5953a1647802ac312a798fe69 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 20 Jun 2016 15:51:25 +0200 Subject: Improve accuracy of timeouts using premature timeouts Improve accuracy of timeouts using a premature timeout then return to sleep with a shorter timeout just before requested timeout. This approach is only used on certain platforms where we know it improves the accuracy of the timeouts, e.g. MacOS X. --- erts/emulator/sys/common/erl_poll.c | 14 ++++++- erts/include/internal/erl_misc_utils.h | 29 +++++++++++++ erts/lib_src/pthread/ethr_event.c | 74 +++++++++++++++++++++++++--------- 3 files changed, 98 insertions(+), 19 deletions(-) (limited to 'erts') diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index e394d84f73..b8a28bcc18 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -75,6 +75,7 @@ #include "erl_driver.h" #include "erl_alloc.h" #include "erl_msacc.h" +#include "erl_misc_utils.h" #if !defined(ERTS_POLL_USE_EPOLL) \ && !defined(ERTS_POLL_USE_DEVPOLL) \ @@ -2132,16 +2133,19 @@ get_timeout(ErtsPollSet ps, if (timeout > (ErtsMonotonicTime) INT_MAX) timeout = (ErtsMonotonicTime) INT_MAX; save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000); break; case 1000000: /* Round up to nearest even micro second */ timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1; save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000); break; case 1000000000: /* Round up to nearest even nano second */ timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1; save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000); break; default: ERTS_INTERNAL_ERROR("Invalid resolution"); @@ -2452,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, } #endif - res = check_fd_events(ps, to, no_fds); + while (1) { + res = check_fd_events(ps, to, no_fds); + if (res != 0) + break; + if (to == ERTS_POLL_NO_TIMEOUT) + break; + if (erts_get_monotonic_time(NULL) >= timeout_time) + break; + } woke_up(ps); diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index a4a5d1d510..55566ddf74 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error); int erts_get_last_win_errno(void); #endif +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +/* + * ERTS_PREMATURE_TIMEOUT() expects time units + * 1000 (millisec), 1000000 (microsec), or + * 1000000000 (nanosec). Might not work properly + * otherwise. + */ +#undef ERTS_USE_PREMATURE_TIMEOUT +#undef ERTS_PREMATURE_TIMEOUT + +#if defined(__DARWIN__) +#define ERTS_USE_PREMATURE_TIMEOUT 1 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \ + ((TMO) >= 1 * ((TU) / 1000) \ + ? ((TMO) >= 20 * ((TU) / 1000) \ + ? 15 * ((TU) / 1000) \ + : ((TMO) >= 5 * ((TU) / 1000) \ + ? 3 * ((TU) / 1000) \ + : 5 * ((TU) / 10000))) \ + : 0) + +#else +#define ERTS_USE_PREMATURE_TIMEOUT 0 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0) +#endif + #endif /* #ifndef ERL_MISC_UTILS_H_ */ diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index eef88d5002..464875570a 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -184,6 +184,8 @@ return_event_on: #include #include +#include "erl_misc_utils.h" + #ifdef __DARWIN__ struct ethr_event_fdsets___ { @@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) ethr_sint32_t val; int res, ulres; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */ + ethr_sint64_t time = 0; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */ -#endif -#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC - struct timespec cond_timeout; + ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */ #endif val = ethr_atomic32_read(&e->state); @@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) if (timeout == 0) return ETIMEDOUT; else { - time = timeout; - switch (e->fd[0]) { - case ETHR_EVENT_INVALID_FD__: #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); + timeout_time = ethr_get_monotonic_time(); + timeout_time += timeout; #endif + switch (e->fd[0]) { + case ETHR_EVENT_INVALID_FD__: + time = timeout; setup_nonblocking_pipe(e); break; #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC case ETHR_EVENT_COND_TIMEDWAIT__: - time += ethr_get_monotonic_time(); - cond_timeout.tv_sec = time / (1000*1000*1000); - cond_timeout.tv_nsec = time % (1000*1000*1000); + time = -1; if (spincount == 0) goto set_event_off_waiter; break; #endif default: + time = timeout; /* Already initialized pipe... */ if (spincount == 0) goto set_select_timeout; -#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); -#endif break; } } @@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) || e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__ #endif ) { +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + struct timespec cond_timeout; +#endif set_event_off_waiter: @@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC if (timeout > 0) { + if (time != timeout_time) { + time = timeout_time; + +#if ERTS_USE_PREMATURE_TIMEOUT + { + ethr_sint64_t rtmo; + + rtmo = timeout_time - ethr_get_monotonic_time(); + if (rtmo <= 0) { + res = ETIMEDOUT; + break; + } + time = timeout_time; + time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000); + } +#endif + + cond_timeout.tv_sec = time / (1000*1000*1000); + cond_timeout.tv_nsec = time % (1000*1000*1000); + } res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout); - if (res == EINTR || res == ETIMEDOUT) + if (res == EINTR + || (res == ETIMEDOUT +#if ERTS_USE_PREMATURE_TIMEOUT + && time == timeout_time +#endif + )) { break; + } } else #endif @@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) struct timeval select_timeout; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - time -= ethr_get_monotonic_time() - start; +#if ERTS_USE_PREMATURE_TIMEOUT + restart_select: +#endif + + time = timeout_time - ethr_get_monotonic_time(); if (time <= 0) return ETIMEDOUT; #endif @@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) */ time = ((time - 1) / 1000) + 1; +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000); +#endif + select_timeout.tv_sec = time / (1000*1000); select_timeout.tv_usec = time % (1000*1000); @@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) goto return_event_on; - +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + if (res == ETIMEDOUT) + goto restart_select; /* Verify timeout */ +#endif } return res; -- cgit v1.2.3