minix/minix/lib/libsys/tickdelay.c
David van Moolenbroek e10ce184e4 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
2015-08-08 16:55:23 +00:00

49 lines
1.6 KiB
C

#include "sysutil.h"
#include <minix/timers.h>
/*===========================================================================*
* tickdelay *
*===========================================================================*/
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
* 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.
*/
clock_t time_left, uptime;
message m;
int r, status;
if (ticks <= 0) return OK; /* check for robustness */
/* 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;
}
/* 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;
}