]> Zhao Yanbai Git Server - minix.git/commitdiff
libsys: make tickdelay(3) more reliable 46/3046/1
authorDavid van Moolenbroek <david@minix3.org>
Sat, 18 Jul 2015 07:52:27 +0000 (09:52 +0200)
committerDavid van Moolenbroek <david@minix3.org>
Sat, 8 Aug 2015 16:55:23 +0000 (16:55 +0000)
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

minix/include/minix/ipc.h
minix/include/minix/syslib.h
minix/kernel/system/do_setalarm.c
minix/lib/libsys/sys_setalarm.c
minix/lib/libsys/tickdelay.c

index c05a5c113798e2886f9e97fec258d47badac7949..6917bde51643a0f13ccf21bf70e4dd3ba5f32f03 100644 (file)
@@ -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);
 
index 82e05a2d0234d362a092dc4673f87430d04b8716..c2ebcf2e441e563124f0584dc513c0befe46a41e 100644 (file)
@@ -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);
 
index ec1625be46bc147122e03ce445b5044fcce73882..1cb1dc7543b12d62ded5743604146dcab428c1a7 100644 (file)
@@ -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);
index 935e75154679b0330498dbdb9a8963bcf542f274..7541b9f0db44d977a50e60b78c3e2a05fed02921 100644 (file)
@@ -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;
 }
index 4b87f2c3af23e3976a3eefd29997b9af926e51ab..c88c2a7925de006df32adaf5ff0f3d43a1bde35c 100644 (file)
@@ -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;
 }