summaryrefslogtreecommitdiff
path: root/thread.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-02-03 19:59:21 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-02-03 19:59:21 +0000
commitc0e2f98f9b8385eb5a9b90ece52b9ba08f0b767e (patch)
tree6f0357c3ff4825a8f0ffc10b6b45a9706d2e87d0 /thread.c
parentc915390b9530c31b4665aacf27c1adfc114f768e (diff)
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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62183 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'thread.c')
-rw-r--r--thread.c109
1 files changed, 40 insertions, 69 deletions
diff --git a/thread.c b/thread.c
index 392dd757be..0179a7ee9a 100644
--- a/thread.c
+++ b/thread.c
@@ -94,7 +94,6 @@ static ID id_locals;
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)
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_wakeup(void)
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)
#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;
+
+ getclockofday(&tvn);
+ *timeout = *to;
+ timeval_sub(timeout, &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;
+ 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 readfds, rb_fdset_t *const writefds,
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,
}
#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) {