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 {
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    /* 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;
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
 | 
			
		||||
	m.m_lsys_krn_sys_setalarm.exp_time =
 | 
			
		||||
		m.m_lsys_krn_sys_setalarm.time_left - ticks;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
    /* 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user