Misc #14431 ยป 0001-thread.c-remove-FP-from-time-calculations.patch
thread.c | ||
---|---|---|
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);
|
||
static const char *thread_status_name(rb_thread_t *th, int detail);
|
||
static void timeval_add(struct timeval *, const struct timeval *);
|
||
static void timeval_sub(struct timeval *, const struct timeval *);
|
||
static int timeval_update_expire(struct timeval *, const struct timeval *);
|
||
static void getclockofday(struct timeval *);
|
||
#define eKillSignal INT2FIX(0)
|
||
#define eTerminateSignal INT2FIX(1)
|
||
... | ... | |
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, ...);
|
||
... | ... | |
}
|
||
}
|
||
static struct timeval double2timeval(double d);
|
||
void
|
||
rb_thread_terminate_all(void)
|
||
{
|
||
... | ... | |
terminate_all(vm, th);
|
||
while (vm_living_thread_num(vm) > 1) {
|
||
struct timeval tv = double2timeval(1.0);
|
||
struct timeval tv = { 1, 0 };
|
||
/*
|
||
* Thread exiting routine in thread_start_func_2 notify
|
||
* me when the last sub-thread exit.
|
||
... | ... | |
}
|
||
/* +infty, for this purpose */
|
||
#define DELAY_INFTY 1E30
|
||
struct join_arg {
|
||
rb_thread_t *target, *waiting;
|
||
double delay;
|
||
struct timeval *limit;
|
||
};
|
||
static VALUE
|
||
... | ... | |
{
|
||
struct join_arg *p = (struct join_arg *)arg;
|
||
rb_thread_t *target_th = p->target, *th = p->waiting;
|
||
const int forever = p->delay == DELAY_INFTY;
|
||
const double limit = forever ? 0 : timeofday() + p->delay;
|
||
struct timeval to;
|
||
if (p->limit) {
|
||
getclockofday(&to);
|
||
timeval_add(&to, p->limit);
|
||
}
|
||
while (target_th->status != THREAD_KILLED) {
|
||
if (forever) {
|
||
if (!p->limit) {
|
||
th->status = THREAD_STOPPED_FOREVER;
|
||
th->vm->sleeper++;
|
||
rb_check_deadlock(th->vm);
|
||
... | ... | |
th->vm->sleeper--;
|
||
}
|
||
else {
|
||
double now = timeofday();
|
||
struct timeval tv;
|
||
if (now > limit) {
|
||
if (timeval_update_expire(p->limit, &to)) {
|
||
thread_debug("thread_join: timeout (thid: %"PRI_THREAD_ID")\n",
|
||
thread_id_str(target_th));
|
||
return Qfalse;
|
||
}
|
||
tv = double2timeval(limit - now);
|
||
th->status = THREAD_STOPPED;
|
||
native_sleep(th, &tv);
|
||
native_sleep(th, p->limit);
|
||
}
|
||
RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
|
||
th->status = THREAD_RUNNABLE;
|
||
... | ... | |
}
|
||
static VALUE
|
||
thread_join(rb_thread_t *target_th, double delay)
|
||
thread_join(rb_thread_t *target_th, struct timeval *tv)
|
||
{
|
||
rb_thread_t *th = GET_THREAD();
|
||
struct join_arg arg;
|
||
... | ... | |
arg.target = target_th;
|
||
arg.waiting = th;
|
||
arg.delay = delay;
|
||
arg.limit = tv;
|
||
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 timeval double2timeval(double);
|
||
/*
|
||
* call-seq:
|
||
* thr.join -> thr
|
||
... | ... | |
static VALUE
|
||
thread_join_m(int argc, VALUE *argv, VALUE self)
|
||
{
|
||
double delay = DELAY_INFTY;
|
||
VALUE limit;
|
||
struct timeval timeval;
|
||
struct timeval *tv = 0;
|
||
rb_scan_args(argc, argv, "01", &limit);
|
||
if (!NIL_P(limit)) {
|
||
delay = rb_num2dbl(limit);
|
||
/*
|
||
* This supports INFINITY and negative values, so we can't use
|
||
* rb_time_interval right now...
|
||
*/
|
||
switch (TYPE(limit)) {
|
||
case T_NIL: break;
|
||
case T_FIXNUM:
|
||
case T_BIGNUM:
|
||
timeval.tv_sec = NUM2TIMET(limit);
|
||
timeval.tv_usec = 0;
|
||
tv = &timeval;
|
||
break;
|
||
default:
|
||
timeval = double2timeval(rb_num2dbl(limit));
|
||
tv = &timeval;
|
||
}
|
||
return thread_join(rb_thread_ptr(self), delay);
|
||
return thread_join(rb_thread_ptr(self), tv);
|
||
}
|
||
/*
|
||
... | ... | |
thread_value(VALUE self)
|
||
{
|
||
rb_thread_t *th = rb_thread_ptr(self);
|
||
thread_join(th, DELAY_INFTY);
|
||
thread_join(th, 0);
|
||
return th->value;
|
||
}
|
||
... | ... | |
}
|
||
}
|
||
static void
|
||
timeval_sub(struct timeval *dst, const struct timeval *tv)
|
||
{
|
||
dst->tv_sec -= tv->tv_sec;
|
||
if ((dst->tv_usec -= tv->tv_usec) < 0) {
|
||
--dst->tv_sec;
|
||
dst->tv_usec += 1000000;
|
||
}
|
||
}
|
||
static int
|
||
timeval_update_expire(struct timeval *tv, const struct timeval *to)
|
||
{
|
||
... | ... | |
"%"PRI_TIMET_PREFIX"d.%.6ld > %"PRI_TIMET_PREFIX"d.%.6ld\n",
|
||
(time_t)to->tv_sec, (long)to->tv_usec,
|
||
(time_t)tvn.tv_sec, (long)tvn.tv_usec);
|
||
tv->tv_sec = to->tv_sec - tvn.tv_sec;
|
||
if ((tv->tv_usec = to->tv_usec - tvn.tv_usec) < 0) {
|
||
--tv->tv_sec;
|
||
tv->tv_usec += 1000000;
|
||
}
|
||
*tv = *to;
|
||
timeval_sub(tv, &tvn);
|
||
return 0;
|
||
}
|
||
... | ... | |
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)
|
||
{
|
||
... | ... | |
#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
|
||
... | ... | |
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) \
|
||
... | ... | |
}
|
||
#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) {
|
||
-
|