From: David van Moolenbroek Date: Sat, 18 Jul 2015 07:52:27 +0000 (+0200) Subject: libsys: make tickdelay(3) more reliable X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=refs%2Fchanges%2F46%2F3046%2F1;p=minix.git libsys: make tickdelay(3) more reliable Previously, there was a tiny chance that tickdelay(3) would return early or that it would fail to reinstate a previous alarm. - sys_setalarm(2) now returns TMR_NEVER instead of 0 for the time left if no previous alarm was set; - sys_setalarm(2) now also returns the current time, to allow the caller to determine whether it got an alarm notification for the alarm it set or for a previous alarm that has just gone off; - tickdelay(3) now makes use of these facilities. Change-Id: Id4f8fe19a61ca8574f43131964e6f0317f613f49 --- diff --git a/minix/include/minix/ipc.h b/minix/include/minix/ipc.h index c05a5c113..6917bde51 100644 --- a/minix/include/minix/ipc.h +++ b/minix/include/minix/ipc.h @@ -1129,9 +1129,10 @@ _ASSERT_MSG_SIZE(mess_lsys_krn_sys_sdevio); typedef struct { clock_t exp_time; clock_t time_left; + clock_t uptime; int abs_time; - uint8_t padding[44]; + uint8_t padding[40]; } mess_lsys_krn_sys_setalarm; _ASSERT_MSG_SIZE(mess_lsys_krn_sys_setalarm); diff --git a/minix/include/minix/syslib.h b/minix/include/minix/syslib.h index 82e05a2d0..c2ebcf2e4 100644 --- a/minix/include/minix/syslib.h +++ b/minix/include/minix/syslib.h @@ -107,7 +107,11 @@ int free_contig(void *addr, size_t len); */ int sys_times(endpoint_t proc_ep, clock_t *user_time, clock_t *sys_time, clock_t *uptime, time_t *boottime); -int sys_setalarm(clock_t exp_time, int abs_time); + +#define sys_setalarm(exp, abs) sys_setalarm2(exp, abs, NULL, NULL) +int sys_setalarm2(clock_t exp_time, int abs_time, clock_t *time_left, + clock_t *uptime); + int sys_vtimer(endpoint_t proc_nr, int which, clock_t *newval, clock_t *oldval); diff --git a/minix/kernel/system/do_setalarm.c b/minix/kernel/system/do_setalarm.c index ec1625be4..1cb1dc754 100644 --- a/minix/kernel/system/do_setalarm.c +++ b/minix/kernel/system/do_setalarm.c @@ -39,12 +39,17 @@ int do_setalarm(struct proc * caller, message * m_ptr) /* Return the ticks left on the previous alarm. */ uptime = get_monotonic(); - if ((tp->tmr_exp_time != TMR_NEVER) && (uptime < tp->tmr_exp_time) ) { + if (tp->tmr_exp_time == TMR_NEVER) { + m_ptr->m_lsys_krn_sys_setalarm.time_left = TMR_NEVER; + } else if (uptime < tp->tmr_exp_time) { m_ptr->m_lsys_krn_sys_setalarm.time_left = (tp->tmr_exp_time - uptime); } else { m_ptr->m_lsys_krn_sys_setalarm.time_left = 0; } + /* For the caller's convenience, also return the current time. */ + m_ptr->m_lsys_krn_sys_setalarm.uptime = uptime; + /* Finally, (re)set the timer depending on the expiration time. */ if (exp_time == 0) { reset_kernel_timer(tp); diff --git a/minix/lib/libsys/sys_setalarm.c b/minix/lib/libsys/sys_setalarm.c index 935e75154..7541b9f0d 100644 --- a/minix/lib/libsys/sys_setalarm.c +++ b/minix/lib/libsys/sys_setalarm.c @@ -1,17 +1,26 @@ #include "syslib.h" -/*===========================================================================* - * sys_setalarm * - *===========================================================================*/ -int sys_setalarm(exp_time, abs_time) -clock_t exp_time; /* expiration time for the alarm */ -int abs_time; /* use absolute or relative expiration time */ -{ -/* Ask the SYSTEM schedule a synchronous alarm for the caller. The process - * number can be SELF if the caller doesn't know its process number. +/* + * Ask the kernel to schedule a synchronous alarm for the caller, using either + * an absolute or a relative number of clock ticks. Optionally return the time + * left on the previous timer (TMR_NEVER if none was set) and the current time. */ - message m; - m.m_lsys_krn_sys_setalarm.exp_time = exp_time; /* the expiration time */ - m.m_lsys_krn_sys_setalarm.abs_time = abs_time; /* time is absolute? */ - return _kernel_call(SYS_SETALARM, &m); +int +sys_setalarm2(clock_t exp_time, int abs_time, clock_t * time_left, + clock_t * uptime) +{ + message m; + int r; + + m.m_lsys_krn_sys_setalarm.exp_time = exp_time; /* expiration time */ + m.m_lsys_krn_sys_setalarm.abs_time = abs_time; /* time is absolute? */ + + if ((r = _kernel_call(SYS_SETALARM, &m)) != OK) + return r; + + if (time_left != NULL) + *time_left = m.m_lsys_krn_sys_setalarm.time_left; + if (uptime != NULL) + *uptime = m.m_lsys_krn_sys_setalarm.uptime; + return OK; } diff --git a/minix/lib/libsys/tickdelay.c b/minix/lib/libsys/tickdelay.c index 4b87f2c3a..c88c2a792 100644 --- a/minix/lib/libsys/tickdelay.c +++ b/minix/lib/libsys/tickdelay.c @@ -8,33 +8,41 @@ int tickdelay(clock_t ticks) { /* This function uses the synchronous alarm to delay for a while. This works * even if a previous synchronous alarm was scheduled, because the remaining - * tick of the previous alarm are returned so that it can be rescheduled. - * Note however that a long tick_delay (longer than the remaining time of the + * ticks of the previous alarm are returned so that it can be rescheduled. + * Note however that a long tick delay (longer than the remaining time of the * previous) alarm will also delay the previous alarm. */ - message m, m_alarm; - int s; + clock_t time_left, uptime; + message m; + int r, status; if (ticks <= 0) return OK; /* check for robustness */ - m.m_lsys_krn_sys_setalarm.exp_time = ticks; /* request message after ticks */ - m.m_lsys_krn_sys_setalarm.abs_time = 0; /* ticks are relative to now */ - s = _kernel_call(SYS_SETALARM, &m); - if (s != OK) return(s); - - sef_receive(CLOCK,&m_alarm); /* await synchronous alarm */ - - /* Check if we must reschedule the current alarm. */ - if (m.m_lsys_krn_sys_setalarm.time_left > 0 && - m.m_lsys_krn_sys_setalarm.time_left != TMR_NEVER) { + /* Set the new alarm while getting the time left on the previous alarm. */ + if ((r = sys_setalarm2(ticks, FALSE, &time_left, &uptime)) != OK) + return r; + + /* Await synchronous alarm. Since an alarm notification may already have + * been dispatched by the time that we set the new alarm, we keep going + * until we actually receive an alarm with a timestamp no earlier than the + * alarm time we expect. + */ + while ((r = ipc_receive(CLOCK, &m, &status)) == OK) { + if (m.m_type == NOTIFY_MESSAGE && + m.m_notify.timestamp >= uptime + ticks) + break; + } - m.m_lsys_krn_sys_setalarm.exp_time = - m.m_lsys_krn_sys_setalarm.time_left - ticks; + /* Check if we must reschedule the previous alarm. */ + if (time_left != TMR_NEVER) { + if (time_left > ticks) + time_left -= ticks; + else + time_left = 1; /* force an alarm */ - if (m.m_lsys_krn_sys_setalarm.exp_time <= 0) - m.m_lsys_krn_sys_setalarm.exp_time = 1; - s = _kernel_call(SYS_SETALARM, &m); + /* There's no point in returning an error from here.. */ + (void)sys_setalarm(time_left, FALSE); } - return(s); + return r; }