diff options
-rw-r--r-- | common.mk | 1 | ||||
-rw-r--r-- | hrtime.h | 120 | ||||
-rw-r--r-- | thread.c | 254 | ||||
-rw-r--r-- | thread_pthread.c | 110 | ||||
-rw-r--r-- | thread_sync.c | 18 | ||||
-rw-r--r-- | thread_win32.c | 11 |
6 files changed, 279 insertions, 235 deletions
@@ -2847,6 +2847,7 @@ thread.$(OBJEXT): {$(VPATH)}defines.h thread.$(OBJEXT): {$(VPATH)}encoding.h thread.$(OBJEXT): {$(VPATH)}eval_intern.h thread.$(OBJEXT): {$(VPATH)}gc.h +thread.$(OBJEXT): {$(VPATH)}hrtime.h thread.$(OBJEXT): {$(VPATH)}id.h thread.$(OBJEXT): {$(VPATH)}intern.h thread.$(OBJEXT): {$(VPATH)}internal.h diff --git a/hrtime.h b/hrtime.h new file mode 100644 index 0000000000..4dc699a9b6 --- /dev/null +++ b/hrtime.h @@ -0,0 +1,120 @@ +#ifndef RB_HRTIME_H +#define RB_HRTIME_H +#include "ruby/ruby.h" +#include <time.h> +#if defined(HAVE_SYS_TIME_H) +# include <sys/time.h> +#endif + +/* + * Hi-res monotonic clock. It is currently nsec resolution, which has over + * 500 years of range. + * + * TBD: Is nsec even necessary? usec resolution seems enough for userspace + * and it'll be suitable for use with devices lasting over 500,000 years + * (maybe some devices designed for long-term space travel) + */ +#define RB_HRTIME_PER_USEC ((rb_hrtime_t)1000) +#define RB_HRTIME_PER_MSEC (RB_HRTIME_PER_USEC * (rb_hrtime_t)1000) +#define RB_HRTIME_PER_SEC (RB_HRTIME_PER_MSEC * (rb_hrtime_t)1000) +#define RB_HRTIME_MAX UINT64_MAX + +/* + * Lets try to support time travelers. Lets assume anybody with a time machine + * also has access to a modern gcc or clang with 128-bit int support + */ +#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL +typedef int128_t rb_hrtime_t; +#else +typedef uint64_t rb_hrtime_t; +#endif + +/* thread.c */ +rb_hrtime_t rb_hrtime_now(void); + +static inline rb_hrtime_t +rb_hrtime_mul(rb_hrtime_t a, rb_hrtime_t b) +{ + rb_hrtime_t c; + +#ifdef HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW + if (__builtin_mul_overflow(a, b, &c)) + return RB_HRTIME_MAX; +#else + if (b != 0 && b > RB_HRTIME_MAX / b) /* overflow */ + return RB_HRTIME_MAX; + c = a * b; +#endif + return c; +} + +static inline rb_hrtime_t +rb_hrtime_add(rb_hrtime_t a, rb_hrtime_t b) +{ + rb_hrtime_t c; + +#ifdef HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW + if (__builtin_add_overflow(a, b, &c)) + return RB_HRTIME_MAX; +#else + c = a + b; + if (c < a) /* overflow */ + return RB_HRTIME_MAX; +#endif + return c; +} + +static inline rb_hrtime_t +rb_timeval2hrtime(const struct timeval *tv) +{ + rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)tv->tv_sec, RB_HRTIME_PER_SEC); + rb_hrtime_t u = rb_hrtime_mul((rb_hrtime_t)tv->tv_usec, RB_HRTIME_PER_USEC); + + return rb_hrtime_add(s, u); +} + +static inline rb_hrtime_t +rb_timespec2hrtime(const struct timespec *ts) +{ + rb_hrtime_t s = rb_hrtime_mul((rb_hrtime_t)ts->tv_sec, RB_HRTIME_PER_SEC); + + return rb_hrtime_add(s, (rb_hrtime_t)ts->tv_nsec); +} + +static inline rb_hrtime_t +rb_msec2hrtime(unsigned long msec) +{ + return rb_hrtime_mul((rb_hrtime_t)msec, RB_HRTIME_PER_MSEC); +} + +static inline rb_hrtime_t +rb_sec2hrtime(time_t sec) +{ + if (sec <= 0) return 0; + + return rb_hrtime_mul((rb_hrtime_t)sec, RB_HRTIME_PER_SEC); +} + +static inline struct timespec * +rb_hrtime2timespec(struct timespec *ts, const rb_hrtime_t *hrt) +{ + if (hrt) { + ts->tv_sec = *hrt / RB_HRTIME_PER_SEC; + ts->tv_nsec = (int32_t)(*hrt % RB_HRTIME_PER_SEC); + return ts; + } + return 0; +} + +static inline struct timeval * +rb_hrtime2timeval(struct timeval *tv, const rb_hrtime_t *hrt) +{ + if (hrt) { + tv->tv_sec = *hrt / RB_HRTIME_PER_SEC; + tv->tv_usec = (int32_t)((*hrt % RB_HRTIME_PER_SEC)/RB_HRTIME_PER_USEC); + + return tv; + } + return 0; +} +#endif /* RB_HRTIME_H */ @@ -74,6 +74,7 @@ #include "internal.h" #include "iseq.h" #include "vm_core.h" +#include "hrtime.h" #ifndef USE_NATIVE_THREAD_PRIORITY #define USE_NATIVE_THREAD_PRIORITY 0 @@ -97,18 +98,14 @@ enum SLEEP_FLAGS { SLEEP_SPURIOUS_CHECK = 0x2 }; -static void sleep_timespec(rb_thread_t *, struct timespec, unsigned int fl); +static void sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl); static void sleep_forever(rb_thread_t *th, unsigned int fl); static void rb_thread_sleep_deadly_allow_spurious_wakeup(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); static const char *thread_status_name(rb_thread_t *th, int detail); -static void timespec_add(struct timespec *, const struct timespec *); -static void timespec_sub(struct timespec *, const struct timespec *); -static int timespec_cmp(const struct timespec *a, const struct timespec *b); -static int timespec_update_expire(struct timespec *, const struct timespec *); -static void getclockofday(struct timespec *); +static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t); NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd)); static int consume_communication_pipe(int fd); static int check_signals_nogvl(rb_thread_t *, int sigwait_fd); @@ -229,40 +226,17 @@ vm_living_thread_num(const rb_vm_t *vm) # endif #endif -static 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; -} - -static struct timeval * -timeval_for(struct timeval *tv, const struct timespec *ts) -{ - if (tv && ts) { - tv->tv_sec = ts->tv_sec; - tv->tv_usec = (int32_t)(ts->tv_nsec / 1000); /* 10**6 < 2**(32-1) */ - return tv; - } - return 0; -} - static void -timeout_prepare(struct timespec **tsp, - struct timespec *ts, struct timespec *end, - const struct timeval *timeout) +timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end, + const struct timeval *timeout) { if (timeout) { - getclockofday(end); - timespec_add(end, timespec_for(ts, timeout)); - *tsp = ts; + *rel = rb_timeval2hrtime(timeout); + *end = rb_hrtime_add(rb_hrtime_now(), *rel); + *to = rel; } else { - *tsp = 0; + *to = 0; } } @@ -596,13 +570,13 @@ rb_thread_terminate_all(void) terminate_all(vm, th); while (vm_living_thread_num(vm) > 1) { - struct timespec ts = { 1, 0 }; + rb_hrtime_t rel = RB_HRTIME_PER_SEC; /* * Thread exiting routine in thread_start_func_2 notify * me when the last sub-thread exit. */ sleeping = 1; - native_sleep(th, &ts); + native_sleep(th, &rel); RUBY_VM_CHECK_INTS_BLOCKING(ec); sleeping = 0; } @@ -931,7 +905,7 @@ rb_thread_create(VALUE (*fn)(ANYARGS), void *arg) struct join_arg { rb_thread_t *target, *waiting; - struct timespec *limit; + rb_hrtime_t *limit; }; static VALUE @@ -960,11 +934,10 @@ thread_join_sleep(VALUE arg) { struct join_arg *p = (struct join_arg *)arg; rb_thread_t *target_th = p->target, *th = p->waiting; - struct timespec end; + rb_hrtime_t end; if (p->limit) { - getclockofday(&end); - timespec_add(&end, p->limit); + end = rb_hrtime_add(*p->limit, rb_hrtime_now()); } while (target_th->status != THREAD_KILLED) { @@ -976,7 +949,7 @@ thread_join_sleep(VALUE arg) th->vm->sleeper--; } else { - if (timespec_update_expire(p->limit, &end)) { + if (hrtime_update_expire(p->limit, end)) { thread_debug("thread_join: timeout (thid: %"PRI_THREAD_ID")\n", thread_id_str(target_th)); return Qfalse; @@ -993,7 +966,7 @@ thread_join_sleep(VALUE arg) } static VALUE -thread_join(rb_thread_t *target_th, struct timespec *ts) +thread_join(rb_thread_t *target_th, rb_hrtime_t *rel) { rb_thread_t *th = GET_THREAD(); struct join_arg arg; @@ -1007,7 +980,7 @@ thread_join(rb_thread_t *target_th, struct timespec *ts) arg.target = target_th; arg.waiting = th; - arg.limit = ts; + arg.limit = rel; thread_debug("thread_join (thid: %"PRI_THREAD_ID", status: %s)\n", thread_id_str(target_th), thread_status_name(target_th, TRUE)); @@ -1052,7 +1025,7 @@ thread_join(rb_thread_t *target_th, struct timespec *ts) return target_th->self; } -static struct timespec *double2timespec(struct timespec *, double); +static rb_hrtime_t *double2hrtime(rb_hrtime_t *, double); /* * call-seq: @@ -1097,8 +1070,7 @@ static VALUE thread_join_m(int argc, VALUE *argv, VALUE self) { VALUE limit; - struct timespec timespec; - struct timespec *ts = 0; + rb_hrtime_t rel, *to = 0; rb_scan_args(argc, argv, "01", &limit); @@ -1109,17 +1081,14 @@ thread_join_m(int argc, VALUE *argv, VALUE self) switch (TYPE(limit)) { case T_NIL: break; case T_FIXNUM: - timespec.tv_sec = NUM2TIMET(limit); - if (timespec.tv_sec < 0) - timespec.tv_sec = 0; - timespec.tv_nsec = 0; - ts = ×pec; + rel = rb_sec2hrtime(NUM2TIMET(limit)); + to = &rel; break; default: - ts = double2timespec(×pec, rb_num2dbl(limit)); + to = double2hrtime(&rel, rb_num2dbl(limit)); } - return thread_join(rb_thread_ptr(self), ts); + return thread_join(rb_thread_ptr(self), to); } /* @@ -1158,8 +1127,8 @@ thread_value(VALUE self) #define TIMESPEC_SEC_MAX TIMET_MAX #define TIMESPEC_SEC_MIN TIMET_MIN -static struct timespec * -double2timespec(struct timespec *ts, double d) +static rb_hrtime_t * +double2hrtime(rb_hrtime_t *hrt, double d) { /* assume timespec.tv_sec has same signedness as time_t */ const double TIMESPEC_SEC_MAX_PLUS_ONE = TIMET_MAX_PLUS_ONE; @@ -1168,18 +1137,31 @@ double2timespec(struct timespec *ts, double d) return NULL; } else if (d <= 0) { - ts->tv_sec = 0; - ts->tv_nsec = 0; + *hrt = 0; } else { - ts->tv_sec = (time_t)d; - ts->tv_nsec = (long)((d - (time_t)d) * 1e9); - if (ts->tv_nsec < 0) { - ts->tv_nsec += (long)1e9; - ts->tv_sec -= 1; - } + *hrt = (rb_hrtime_t)(d * (double)RB_HRTIME_PER_SEC); } - return ts; + return hrt; +} + +static void +getclockofday(struct timespec *ts) +{ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) + return; +#endif + rb_timespec_now(ts); +} + +rb_hrtime_t +rb_hrtime_now(void) +{ + struct timespec ts; + + getclockofday(&ts); + return rb_timespec2hrtime(&ts); } static void @@ -1208,102 +1190,39 @@ sleep_forever(rb_thread_t *th, unsigned int fl) th->status = prev_status; } -static void -getclockofday(struct timespec *ts) -{ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) - return; -#endif - rb_timespec_now(ts); -} - -static void -timespec_add(struct timespec *dst, const struct timespec *ts) -{ - if (TIMESPEC_SEC_MAX - ts->tv_sec < dst->tv_sec) - dst->tv_sec = TIMESPEC_SEC_MAX; - else - dst->tv_sec += ts->tv_sec; - if ((dst->tv_nsec += ts->tv_nsec) >= 1000000000) { - if (dst->tv_sec == TIMESPEC_SEC_MAX) { - dst->tv_nsec = 999999999; - } - else { - dst->tv_sec++; - dst->tv_nsec -= 1000000000; - } - } -} - -static void -timespec_sub(struct timespec *dst, const struct timespec *tv) -{ - dst->tv_sec -= tv->tv_sec; - if ((dst->tv_nsec -= tv->tv_nsec) < 0) { - --dst->tv_sec; - dst->tv_nsec += 1000000000; - } -} - -static int -timespec_cmp(const struct timespec *a, const struct timespec *b) -{ - if (a->tv_sec > b->tv_sec) { - return 1; - } - else if (a->tv_sec < b->tv_sec) { - return -1; - } - else { - if (a->tv_nsec > b->tv_nsec) { - return 1; - } - else if (a->tv_nsec < b->tv_nsec) { - return -1; - } - return 0; - } -} - /* * @end is the absolute time when @ts is set to expire * Returns true if @end has past * Updates @ts and returns false otherwise */ static int -timespec_update_expire(struct timespec *ts, const struct timespec *end) -{ - struct timespec now; - - getclockofday(&now); - if (timespec_cmp(&now, end) >= 0) return 1; - thread_debug("timespec_update_expire: " - "%"PRI_TIMET_PREFIX"d.%.6ld > %"PRI_TIMET_PREFIX"d.%.6ld\n", - (time_t)end->tv_sec, (long)end->tv_nsec, - (time_t)now.tv_sec, (long)now.tv_nsec); - *ts = *end; - timespec_sub(ts, &now); +hrtime_update_expire(rb_hrtime_t *timeout, const rb_hrtime_t end) +{ + rb_hrtime_t now = rb_hrtime_now(); + + if (now > end) return 1; + thread_debug("hrtime_update_expire: " + "%"PRI_64_PREFIX"u > %"PRI_64_PREFIX"u\n", + end, now); + *timeout = end - now; return 0; } static void -sleep_timespec(rb_thread_t *th, struct timespec ts, unsigned int fl) +sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel, unsigned int fl) { - struct timespec end; enum rb_thread_status prev_status = th->status; int woke; + rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), rel); - getclockofday(&end); - timespec_add(&end, &ts); th->status = THREAD_STOPPED; RUBY_VM_CHECK_INTS_BLOCKING(th->ec); while (th->status == THREAD_STOPPED) { - native_sleep(th, &ts); + native_sleep(th, &rel); woke = vm_check_ints_blocking(th->ec); if (woke && !(fl & SLEEP_SPURIOUS_CHECK)) break; - if (timespec_update_expire(&ts, &end)) + if (hrtime_update_expire(&rel, end)) break; } th->status = prev_status; @@ -1334,10 +1253,8 @@ void rb_thread_wait_for(struct timeval time) { rb_thread_t *th = GET_THREAD(); - struct timespec ts; - timespec_for(&ts, &time); - sleep_timespec(th, ts, SLEEP_SPURIOUS_CHECK); + sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK); } /* @@ -3848,8 +3765,7 @@ rb_fd_set(int fd, rb_fdset_t *set) #endif static int -wait_retryable(int *result, int errnum, struct timespec *timeout, - const struct timespec *end) +wait_retryable(int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end) { if (*result < 0) { switch (errnum) { @@ -3858,9 +3774,8 @@ wait_retryable(int *result, int errnum, struct timespec *timeout, case ERESTART: #endif *result = 0; - if (timeout && timespec_update_expire(timeout, end)) { - timeout->tv_sec = 0; - timeout->tv_nsec = 0; + if (rel && hrtime_update_expire(rel, end)) { + *rel = 0; } return TRUE; } @@ -3868,8 +3783,8 @@ wait_retryable(int *result, int errnum, struct timespec *timeout, } else if (*result == 0) { /* check for spurious wakeup */ - if (timeout) { - return !timespec_update_expire(timeout, end); + if (rel) { + return !hrtime_update_expire(rel, end); } return TRUE; } @@ -3906,15 +3821,15 @@ select_set_free(VALUE p) return Qfalse; } -static const struct timespec * -sigwait_timeout(rb_thread_t *th, int sigwait_fd, const struct timespec *orig, +static const rb_hrtime_t * +sigwait_timeout(rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *orig, int *drained_p) { - static const struct timespec quantum = { 0, TIME_QUANTUM_USEC * 1000 }; + static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000; if (sigwait_fd >= 0 && (!ubf_threads_empty() || BUSY_WAIT_SIGNALS)) { *drained_p = check_signals_nogvl(th, sigwait_fd); - if (!orig || timespec_cmp(orig, &quantum) > 0) + if (!orig || *orig > quantum) return &quantum; } @@ -3927,11 +3842,9 @@ do_select(VALUE p) struct select_set *set = (struct select_set *)p; int MAYBE_UNUSED(result); int lerrno; - struct timespec ts, end, *tsp; - const struct timespec *to; - struct timeval tv; + rb_hrtime_t *to, rel, end; - timeout_prepare(&tsp, &ts, &end, set->timeout); + timeout_prepare(&to, &rel, &end, set->timeout); #define restore_fdset(dst, src) \ ((dst) ? rb_fd_dup(dst, src) : (void)0) #define do_select_update() \ @@ -3945,9 +3858,12 @@ do_select(VALUE p) lerrno = 0; BLOCKING_REGION(set->th, { - to = sigwait_timeout(set->th, set->sigwait_fd, tsp, &drained); + const rb_hrtime_t *sto; + struct timeval tv; + + sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained); result = native_fd_select(set->max, set->rset, set->wset, set->eset, - timeval_for(&tv, to), set->th); + rb_hrtime2timeval(&tv, sto), set->th); if (result < 0) lerrno = errno; }, set->sigwait_fd >= 0 ? ubf_sigwait : ubf_select, set->th, FALSE); @@ -3958,7 +3874,7 @@ do_select(VALUE p) } RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */ - } while (wait_retryable(&result, lerrno, tsp, &end) && do_select_update()); + } while (wait_retryable(&result, lerrno, to, end) && do_select_update()); if (result < 0) { errno = lerrno; @@ -4082,14 +3998,13 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *timeout) { struct pollfd fds[2]; int result = 0, lerrno; - struct timespec ts, end, *tsp; - const struct timespec *to; + rb_hrtime_t *to, rel, end; int drained; rb_thread_t *th = GET_THREAD(); nfds_t nfds; rb_unblock_function_t *ubf; - timeout_prepare(&tsp, &ts, &end, timeout); + timeout_prepare(&to, &rel, &end, timeout); fds[0].fd = fd; fds[0].events = (short)events; do { @@ -4109,8 +4024,11 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *timeout) lerrno = 0; BLOCKING_REGION(th, { - to = sigwait_timeout(th, fds[1].fd, tsp, &drained); - result = ppoll(fds, nfds, to, NULL); + const rb_hrtime_t *sto; + struct timespec ts; + + sto = sigwait_timeout(th, fds[1].fd, to, &drained); + result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), NULL); if (result < 0) lerrno = errno; }, ubf, th, FALSE); @@ -4124,7 +4042,7 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *timeout) rb_sigwait_fd_migrate(th->vm); } RUBY_VM_CHECK_INTS_BLOCKING(th->ec); - } while (wait_retryable(&result, lerrno, tsp, &end)); + } while (wait_retryable(&result, lerrno, to, end)); if (result < 0) { errno = lerrno; diff --git a/thread_pthread.c b/thread_pthread.c index 52e2a049f1..209ed6781c 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -123,9 +123,9 @@ static void clear_thread_cache_altstack(void); static void ubf_wakeup_all_threads(void); static int ubf_threads_empty(void); static int native_cond_timedwait(rb_nativethread_cond_t *, pthread_mutex_t *, - const struct timespec *); -static const struct timespec *sigwait_timeout(rb_thread_t *, int sigwait_fd, - const struct timespec *, + const rb_hrtime_t *abs); +static const rb_hrtime_t *sigwait_timeout(rb_thread_t *, int sigwait_fd, + const rb_hrtime_t *, int *drained_p); static void ubf_timer_disarm(void); static void threadptr_trap_interrupt(rb_thread_t *); @@ -159,8 +159,7 @@ static const void *const condattr_monotonic = NULL; #define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000) #define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000) -static struct timespec native_cond_timeout(rb_nativethread_cond_t *, - struct timespec rel); +static rb_hrtime_t native_cond_timeout(rb_nativethread_cond_t *, rb_hrtime_t); /* * Designate the next gvl.timer thread, favor the last thread in @@ -186,19 +185,17 @@ designate_timer_thread(rb_vm_t *vm) static void do_gvl_timer(rb_vm_t *vm, rb_thread_t *th) { - static struct timespec ts; + static rb_hrtime_t abs; native_thread_data_t *nd = &th->native_thread_data; /* take over wakeups from UBF_TIMER */ ubf_timer_disarm(); if (vm->gvl.timer_err == ETIMEDOUT) { - ts.tv_sec = 0; - ts.tv_nsec = TIME_QUANTUM_NSEC; - ts = native_cond_timeout(&nd->cond.gvlq, ts); + abs = native_cond_timeout(&nd->cond.gvlq, TIME_QUANTUM_NSEC); } vm->gvl.timer = th; - vm->gvl.timer_err = native_cond_timedwait(&nd->cond.gvlq, &vm->gvl.lock, &ts); + vm->gvl.timer_err = native_cond_timedwait(&nd->cond.gvlq, &vm->gvl.lock, &abs); vm->gvl.timer = 0; ubf_wakeup_all_threads(); @@ -499,9 +496,11 @@ rb_native_cond_wait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex) } static int -native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *ts) +native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, + const rb_hrtime_t *abs) { int r; + struct timespec ts; /* * An old Linux may return EINTR. Even though POSIX says @@ -510,7 +509,7 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, cons * Let's hide it from arch generic code. */ do { - r = pthread_cond_timedwait(cond, mutex, ts); + r = pthread_cond_timedwait(cond, mutex, rb_hrtime2timespec(&ts, abs)); } while (r == EINTR); if (r != 0 && r != ETIMEDOUT) { @@ -520,20 +519,18 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, cons return r; } -static struct timespec -native_cond_timeout(rb_nativethread_cond_t *cond, struct timespec timeout_rel) +static rb_hrtime_t +native_cond_timeout(rb_nativethread_cond_t *cond, const rb_hrtime_t rel) { - struct timespec abs; - if (condattr_monotonic) { - getclockofday(&abs); + return rb_hrtime_add(rb_hrtime_now(), rel); } else { - rb_timespec_now(&abs); - } - timespec_add(&abs, &timeout_rel); + struct timespec ts; - return abs; + rb_timespec_now(&ts); + return rb_hrtime_add(rb_timespec2hrtime(&ts), rel); + } } #define native_cleanup_push pthread_cleanup_push @@ -1056,13 +1053,13 @@ thread_cache_reset(void) * worst case network latency across the globe) without wasting memory */ #ifndef THREAD_CACHE_TIME -# define THREAD_CACHE_TIME 3 +# define THREAD_CACHE_TIME ((rb_hrtime_t)3 * RB_HRTIME_PER_SEC) #endif static rb_thread_t * register_cached_thread_and_wait(void *altstack) { - struct timespec end = { THREAD_CACHE_TIME, 0 }; + rb_hrtime_t end = THREAD_CACHE_TIME; struct cached_thread_entry entry; rb_native_cond_initialize(&entry.cond); @@ -1218,28 +1215,20 @@ ubf_pthread_cond_signal(void *ptr) } static void -native_cond_sleep(rb_thread_t *th, struct timespec *timeout_rel) +native_cond_sleep(rb_thread_t *th, rb_hrtime_t *rel) { - struct timespec timeout; rb_nativethread_lock_t *lock = &th->interrupt_lock; rb_nativethread_cond_t *cond = &th->native_thread_data.cond.intr; - if (timeout_rel) { - /* Solaris cond_timedwait() return EINVAL if an argument is greater than - * current_time + 100,000,000. So cut up to 100,000,000. This is - * considered as a kind of spurious wakeup. The caller to native_sleep - * should care about spurious wakeup. - * - * See also [Bug #1341] [ruby-core:29702] - * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html - */ - if (timeout_rel->tv_sec > 100000000) { - timeout_rel->tv_sec = 100000000; - timeout_rel->tv_nsec = 0; - } - - timeout = native_cond_timeout(cond, *timeout_rel); - } + /* Solaris cond_timedwait() return EINVAL if an argument is greater than + * current_time + 100,000,000. So cut up to 100,000,000. This is + * considered as a kind of spurious wakeup. The caller to native_sleep + * should care about spurious wakeup. + * + * See also [Bug #1341] [ruby-core:29702] + * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html + */ + const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC; GVL_UNLOCK_BEGIN(th); { @@ -1252,10 +1241,19 @@ native_cond_sleep(rb_thread_t *th, struct timespec *timeout_rel) thread_debug("native_sleep: interrupted before sleep\n"); } else { - if (!timeout_rel) + if (!rel) { rb_native_cond_wait(cond, lock); - else - native_cond_timedwait(cond, lock, &timeout); + } + else { + rb_hrtime_t end; + + if (*rel > max) { + *rel = max; + } + + end = native_cond_timeout(cond, *rel); + native_cond_timedwait(cond, lock, &end); + } } th->unblock.func = 0; @@ -1970,15 +1968,12 @@ rb_sigwait_sleep(rb_thread_t *th, int sigwait_fd, const struct timespec *ts) check_signals_nogvl(th, sigwait_fd); } else { - struct timespec end, diff; - const struct timespec *to; + rb_hrtime_t rel, end; int n = 0; if (ts) { - getclockofday(&end); - timespec_add(&end, ts); - diff = *ts; - ts = &diff; + rel = rb_timespec2hrtime(ts); + end = rb_hrtime_add(rb_hrtime_now(), rel); } /* * tricky: this needs to return on spurious wakeup (no auto-retry). @@ -1986,21 +1981,23 @@ rb_sigwait_sleep(rb_thread_t *th, int sigwait_fd, const struct timespec *ts) * wakeups, so we care about the result of consume_communication_pipe */ for (;;) { - to = sigwait_timeout(th, sigwait_fd, ts, &n); + const rb_hrtime_t *sto = sigwait_timeout(th, sigwait_fd, &rel, &n); + struct timespec tmp; + if (n) return; - n = ppoll(&pfd, 1, to, 0); + n = ppoll(&pfd, 1, rb_hrtime2timespec(&tmp, sto), 0); if (check_signals_nogvl(th, sigwait_fd)) return; if (n || (th && RUBY_VM_INTERRUPTED(th->ec))) return; - if (ts && timespec_update_expire(&diff, &end)) + if (ts && hrtime_update_expire(&rel, end)) return; } } } static void -native_sleep(rb_thread_t *th, struct timespec *timeout_rel) +native_sleep(rb_thread_t *th, rb_hrtime_t *rel) { int sigwait_fd = rb_sigwait_fd_get(th); @@ -2012,7 +2009,8 @@ native_sleep(rb_thread_t *th, struct timespec *timeout_rel) GVL_UNLOCK_BEGIN(th); if (!RUBY_VM_INTERRUPTED(th->ec)) { - rb_sigwait_sleep(th, sigwait_fd, timeout_rel); + struct timespec ts; + rb_sigwait_sleep(th, sigwait_fd, rb_hrtime2timespec(&ts, rel)); } else { check_signals_nogvl(th, sigwait_fd); @@ -2023,7 +2021,7 @@ native_sleep(rb_thread_t *th, struct timespec *timeout_rel) rb_sigwait_fd_migrate(th->vm); } else { - native_cond_sleep(th, timeout_rel); + native_cond_sleep(th, rel); } } diff --git a/thread_sync.c b/thread_sync.c index 76632ebe5f..4ed559ad67 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -248,8 +248,8 @@ do_mutex_lock(VALUE self, int interruptible_p) while (mutex->th != th) { enum rb_thread_status prev_status = th->status; - struct timespec *timeout = 0; - struct timespec ts = { 0, 100000000 }; /* 100ms */ + rb_hrtime_t *timeout = 0; + rb_hrtime_t rel = rb_msec2hrtime(100); th->status = THREAD_STOPPED_FOREVER; th->locking_mutex = self; @@ -261,7 +261,7 @@ do_mutex_lock(VALUE self, int interruptible_p) */ if ((vm_living_thread_num(th->vm) == th->vm->sleeper) && !patrol_thread) { - timeout = &ts; + timeout = &rel; patrol_thread = th; } @@ -458,8 +458,9 @@ rb_mutex_sleep_forever(VALUE time) static VALUE rb_mutex_wait_for(VALUE time) { - struct timespec *t = (struct timespec*)time; - sleep_timespec(GET_THREAD(), *t, 0); /* permit spurious check */ + rb_hrtime_t *rel = (rb_hrtime_t *)time; + /* permit spurious check */ + sleep_hrtime(GET_THREAD(), *rel, 0); return Qnil; } @@ -472,16 +473,17 @@ rb_mutex_sleep(VALUE self, VALUE timeout) if (!NIL_P(timeout)) { t = rb_time_interval(timeout); } + rb_mutex_unlock(self); beg = time(0); if (NIL_P(timeout)) { rb_ensure(rb_mutex_sleep_forever, Qnil, mutex_lock_uninterruptible, self); } else { - struct timespec ts; - VALUE tsp = (VALUE)timespec_for(&ts, &t); + rb_hrtime_t rel = rb_timeval2hrtime(&t); - rb_ensure(rb_mutex_wait_for, tsp, mutex_lock_uninterruptible, self); + rb_ensure(rb_mutex_wait_for, (VALUE)&rel, + mutex_lock_uninterruptible, self); } RUBY_VM_CHECK_INTS_BLOCKING(GET_EC()); end = time(0) - beg; diff --git a/thread_win32.c b/thread_win32.c index af9d3517fa..83896d81ff 100644 --- a/thread_win32.c +++ b/thread_win32.c @@ -276,11 +276,16 @@ rb_w32_Sleep(unsigned long msec) return ret; } +static DWORD +hrtime2msec(rb_hrtime_t hrt) +{ + return (DWORD)hrt / (DWORD)RB_HRTIME_PER_MSEC; +} + static void -native_sleep(rb_thread_t *th, struct timespec *ts) +native_sleep(rb_thread_t *th, rb_hrtime_t *rel) { - const volatile DWORD msec = (ts) ? - (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000) : INFINITE; + const volatile DWORD msec = rel ? hrtime2msec(*rel) : INFINITE; GVL_UNLOCK_BEGIN(th); { |