Project

General

Profile

Misc #15014 ยป 0001-thread.c-use-rb_hrtime_t-scalar-for-high-resolution-.patch

normalperson (Eric Wong), 08/20/2018 11:29 PM

View differences:

common.mk
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
hrtime.h
#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
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 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;
}
static inline void
rb_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 rb_hrtime_t
rb_hrtime_now(void)
{
struct timespec ts;
rb_getclockofday(&ts);
return rb_timespec2hrtime(&ts);
}
#endif /* RB_HRTIME_H */
thread.c
#include "internal.h"
#include "iseq.h"
#include "vm_core.h"
#include "hrtime.h"
#ifndef USE_NATIVE_THREAD_PRIORITY
#define USE_NATIVE_THREAD_PRIORITY 0
......
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);
......
# 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;
}
}
......
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;
}
......
struct join_arg {
rb_thread_t *target, *waiting;
struct timespec *limit;
rb_hrtime_t *limit;
};
static VALUE
......
{
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) {
......
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;
......
}
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;
......
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));
......
return target_th->self;
}
static struct timespec *double2timespec(struct timespec *, double);
static rb_hrtime_t *double2hrtime(rb_hrtime_t *, double);
/*
* call-seq:
......
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);
......
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 = &timespec;
rel = rb_sec2hrtime(NUM2TIMET(limit));
to = &rel;
break;
default:
ts = double2timespec(&timespec, 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);
}
/*
......
#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;
......
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
......
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;
......
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);
}
/*
......
#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) {
......
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;
}
......
}
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;
}
......
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;
}
......
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() \
......
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);
......
}
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;
......
{
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 {
......
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);
......
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;
thread_pthread.c
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 *);
......
#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
......
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();
......
}
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
......
* 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) {
......
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
......
* 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);
......
}
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);
{
......
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;
......
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).
......
* 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);
......
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);
......
rb_sigwait_fd_migrate(th->vm);
}
else {
native_cond_sleep(th, timeout_rel);
native_cond_sleep(th, rel);
}
}
thread_sync.c
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;
......
*/
if ((vm_living_thread_num(th->vm) == th->vm->sleeper) &&
!patrol_thread) {
timeout = &ts;
timeout = &rel;
patrol_thread = th;
}
......
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;
}
......
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;
thread_win32.c
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);
{
-
    (1-1/1)