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
This commit is contained in:
parent
8731fd1c47
commit
e10ce184e4
|
@ -1129,9 +1129,10 @@ _ASSERT_MSG_SIZE(mess_lsys_krn_sys_sdevio);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
clock_t exp_time;
|
clock_t exp_time;
|
||||||
clock_t time_left;
|
clock_t time_left;
|
||||||
|
clock_t uptime;
|
||||||
int abs_time;
|
int abs_time;
|
||||||
|
|
||||||
uint8_t padding[44];
|
uint8_t padding[40];
|
||||||
} mess_lsys_krn_sys_setalarm;
|
} mess_lsys_krn_sys_setalarm;
|
||||||
_ASSERT_MSG_SIZE(mess_lsys_krn_sys_setalarm);
|
_ASSERT_MSG_SIZE(mess_lsys_krn_sys_setalarm);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
int sys_times(endpoint_t proc_ep, clock_t *user_time, clock_t *sys_time,
|
||||||
clock_t *uptime, time_t *boottime);
|
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
|
int sys_vtimer(endpoint_t proc_nr, int which, clock_t *newval, clock_t
|
||||||
*oldval);
|
*oldval);
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,17 @@ int do_setalarm(struct proc * caller, message * m_ptr)
|
||||||
|
|
||||||
/* Return the ticks left on the previous alarm. */
|
/* Return the ticks left on the previous alarm. */
|
||||||
uptime = get_monotonic();
|
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);
|
m_ptr->m_lsys_krn_sys_setalarm.time_left = (tp->tmr_exp_time - uptime);
|
||||||
} else {
|
} else {
|
||||||
m_ptr->m_lsys_krn_sys_setalarm.time_left = 0;
|
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. */
|
/* Finally, (re)set the timer depending on the expiration time. */
|
||||||
if (exp_time == 0) {
|
if (exp_time == 0) {
|
||||||
reset_kernel_timer(tp);
|
reset_kernel_timer(tp);
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
#include "syslib.h"
|
#include "syslib.h"
|
||||||
|
|
||||||
/*===========================================================================*
|
/*
|
||||||
* sys_setalarm *
|
* 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
|
||||||
int sys_setalarm(exp_time, abs_time)
|
* left on the previous timer (TMR_NEVER if none was set) and the current 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.
|
|
||||||
*/
|
*/
|
||||||
message m;
|
int
|
||||||
m.m_lsys_krn_sys_setalarm.exp_time = exp_time; /* the expiration time */
|
sys_setalarm2(clock_t exp_time, int abs_time, clock_t * time_left,
|
||||||
m.m_lsys_krn_sys_setalarm.abs_time = abs_time; /* time is absolute? */
|
clock_t * uptime)
|
||||||
return _kernel_call(SYS_SETALARM, &m);
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,33 +8,41 @@ int tickdelay(clock_t ticks)
|
||||||
{
|
{
|
||||||
/* This function uses the synchronous alarm to delay for a while. This works
|
/* This function uses the synchronous alarm to delay for a while. This works
|
||||||
* even if a previous synchronous alarm was scheduled, because the remaining
|
* even if a previous synchronous alarm was scheduled, because the remaining
|
||||||
* tick of the previous alarm are returned so that it can be rescheduled.
|
* 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
|
* Note however that a long tick delay (longer than the remaining time of the
|
||||||
* previous) alarm will also delay the previous alarm.
|
* previous) alarm will also delay the previous alarm.
|
||||||
*/
|
*/
|
||||||
message m, m_alarm;
|
clock_t time_left, uptime;
|
||||||
int s;
|
message m;
|
||||||
|
int r, status;
|
||||||
|
|
||||||
if (ticks <= 0) return OK; /* check for robustness */
|
if (ticks <= 0) return OK; /* check for robustness */
|
||||||
|
|
||||||
m.m_lsys_krn_sys_setalarm.exp_time = ticks; /* request message after ticks */
|
/* Set the new alarm while getting the time left on the previous alarm. */
|
||||||
m.m_lsys_krn_sys_setalarm.abs_time = 0; /* ticks are relative to now */
|
if ((r = sys_setalarm2(ticks, FALSE, &time_left, &uptime)) != OK)
|
||||||
s = _kernel_call(SYS_SETALARM, &m);
|
return r;
|
||||||
if (s != OK) return(s);
|
|
||||||
|
|
||||||
sef_receive(CLOCK,&m_alarm); /* await synchronous alarm */
|
/* Await synchronous alarm. Since an alarm notification may already have
|
||||||
|
* been dispatched by the time that we set the new alarm, we keep going
|
||||||
/* Check if we must reschedule the current alarm. */
|
* until we actually receive an alarm with a timestamp no earlier than the
|
||||||
if (m.m_lsys_krn_sys_setalarm.time_left > 0 &&
|
* alarm time we expect.
|
||||||
m.m_lsys_krn_sys_setalarm.time_left != TMR_NEVER) {
|
*/
|
||||||
|
while ((r = ipc_receive(CLOCK, &m, &status)) == OK) {
|
||||||
m.m_lsys_krn_sys_setalarm.exp_time =
|
if (m.m_type == NOTIFY_MESSAGE &&
|
||||||
m.m_lsys_krn_sys_setalarm.time_left - ticks;
|
m.m_notify.timestamp >= uptime + ticks)
|
||||||
|
break;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return(s);
|
/* 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 */
|
||||||
|
|
||||||
|
/* There's no point in returning an error from here.. */
|
||||||
|
(void)sys_setalarm(time_left, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue