ruby-changes:50065
From: normal <ko1@a...>
Date: Sun, 4 Feb 2018 05:08:49 +0900 (JST)
Subject: [ruby-changes:50065] normal:r62183 (trunk): thread.c: avoid FP in C-API time calculations
normal 2018-02-04 04:59:21 +0900 (Sun, 04 Feb 2018) New Revision: 62183 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62183 Log: thread.c: avoid FP in C-API time calculations FP arithmetic can lose precision in some cases leading to premature wakeup and wasting CPU cycles. Modified files: trunk/thread.c Index: thread.c =================================================================== --- thread.c (revision 62182) +++ thread.c (revision 62183) @@ -94,7 +94,6 @@ static ID id_locals; https://github.com/ruby/ruby/blob/trunk/thread.c#L94 static void sleep_timeval(rb_thread_t *th, struct timeval time, int spurious_check); static void sleep_forever(rb_thread_t *th, int nodeadlock, int spurious_check); static void rb_thread_sleep_deadly_allow_spurious_wakeup(void); -static double timeofday(void); static int rb_threadptr_dead(rb_thread_t *th); static void rb_check_deadlock(rb_vm_t *vm); static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th); @@ -201,6 +200,17 @@ vm_living_thread_num(rb_vm_t *vm) https://github.com/ruby/ruby/blob/trunk/thread.c#L200 return vm->living_thread_num; } +static inline struct timespec * +timespec_for(struct timespec *ts, const struct timeval *tv) +{ + if (tv) { + ts->tv_sec = tv->tv_sec; + ts->tv_nsec = tv->tv_usec * 1000; + return ts; + } + return 0; +} + #if THREAD_DEBUG #ifdef HAVE_VA_ARGS_MACRO void rb_thread_debug(const char *file, int line, const char *fmt, ...); @@ -1245,24 +1255,6 @@ rb_thread_sleep_deadly_allow_spurious_wa https://github.com/ruby/ruby/blob/trunk/thread.c#L1255 sleep_forever(GET_THREAD(), TRUE, FALSE); } -static double -timeofday(void) -{ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec tp; - - if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { - return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; - } - else -#endif - { - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; - } -} - void rb_thread_wait_for(struct timeval time) { @@ -3763,17 +3755,20 @@ retryable(int e) https://github.com/ruby/ruby/blob/trunk/thread.c#L3755 #define restore_fdset(fds1, fds2) \ ((fds1) ? rb_fd_dup(fds1, fds2) : (void)0) -static inline void -update_timeval(struct timeval *timeout, double limit) +static inline int +update_timeval(struct timeval *timeout, const struct timeval *to) { if (timeout) { - double d = limit - timeofday(); + struct timeval tvn; - timeout->tv_sec = (time_t)d; - timeout->tv_usec = (int)((d-(double)timeout->tv_sec)*1e6); - if (timeout->tv_sec < 0) timeout->tv_sec = 0; - if (timeout->tv_usec < 0) timeout->tv_usec = 0; + getclockofday(&tvn); + *timeout = *to; + timeval_sub(timeout, &tvn); + + if (timeout->tv_sec < 0) timeout->tv_sec = 0; + if (timeout->tv_usec < 0) timeout->tv_usec = 0; } + return TRUE; } static int @@ -3785,22 +3780,18 @@ do_select(int n, rb_fdset_t *const readf https://github.com/ruby/ruby/blob/trunk/thread.c#L3780 rb_fdset_t MAYBE_UNUSED(orig_read); rb_fdset_t MAYBE_UNUSED(orig_write); rb_fdset_t MAYBE_UNUSED(orig_except); - double limit = 0; - struct timeval wait_rest; + struct timeval to; rb_thread_t *th = GET_THREAD(); #define do_select_update() \ (restore_fdset(readfds, &orig_read), \ restore_fdset(writefds, &orig_write), \ restore_fdset(exceptfds, &orig_except), \ - update_timeval(timeout, limit), \ - TRUE) + update_timeval(timeout, &to)) if (timeout) { - limit = timeofday(); - limit += (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6; - wait_rest = *timeout; - timeout = &wait_rest; + getclockofday(&to); + timeval_add(&to, timeout); } #define fd_init_copy(f) \ @@ -3934,57 +3925,37 @@ ppoll(struct pollfd *fds, nfds_t nfds, https://github.com/ruby/ruby/blob/trunk/thread.c#L3925 } #endif -static inline void -update_timespec(struct timespec *timeout, double limit) -{ - if (timeout) { - double d = limit - timeofday(); - - timeout->tv_sec = (long)d; - timeout->tv_nsec = (long)((d-(double)timeout->tv_sec)*1e9); - if (timeout->tv_sec < 0) timeout->tv_sec = 0; - if (timeout->tv_nsec < 0) timeout->tv_nsec = 0; - } -} - /* * returns a mask of events */ int -rb_wait_for_single_fd(int fd, int events, struct timeval *tv) +rb_wait_for_single_fd(int fd, int events, struct timeval *timeout) { struct pollfd fds; int result = 0, lerrno; - double limit = 0; struct timespec ts; - struct timespec *timeout = NULL; + struct timeval to; rb_thread_t *th = GET_THREAD(); -#define poll_update() \ - (update_timespec(timeout, limit), \ - TRUE) - - if (tv) { - ts.tv_sec = tv->tv_sec; - ts.tv_nsec = tv->tv_usec * 1000; - limit = timeofday(); - limit += (double)tv->tv_sec + (double)tv->tv_usec * 1e-6; - timeout = &ts; + if (timeout) { + getclockofday(&to); + timeval_add(&to, timeout); } fds.fd = fd; fds.events = (short)events; do { - fds.revents = 0; - lerrno = 0; - BLOCKING_REGION({ - result = ppoll(&fds, 1, timeout, NULL); - if (result < 0) lerrno = errno; - }, ubf_select, th, FALSE); - - RUBY_VM_CHECK_INTS_BLOCKING(th->ec); - } while (result < 0 && retryable(errno = lerrno) && poll_update()); + fds.revents = 0; + lerrno = 0; + BLOCKING_REGION({ + result = ppoll(&fds, 1, timespec_for(&ts, timeout), NULL); + if (result < 0) lerrno = errno; + }, ubf_select, th, FALSE); + + RUBY_VM_CHECK_INTS_BLOCKING(th->ec); + } while (result < 0 && retryable(errno = lerrno) && + update_timeval(timeout, &to)); if (result < 0) return -1; if (fds.revents & POLLNVAL) { -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/