chiark / gitweb /
mtimeout.c: Restructure timeout handling to use explicit machine.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 15 Dec 2011 00:55:01 +0000 (00:55 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 15 Dec 2011 01:12:43 +0000 (01:12 +0000)
An upcoming change makes the timeout handling rather more fiddly.
Rather than have a huge pile of code to make it all work, encode the
timeout behaviour as explicit instructions to a simple virtual machine.
The instructions are assembled using preprocessor hacking.

mtimeout.c

index 38723ae14e8d210a4531ce7b06d75d5eb44c32c1..055ef7e5d98433f19fc8c22d054abc47c7a6659f 100644 (file)
@@ -314,42 +314,73 @@ Options:\n\
 
 /* --- @timeout@ --- *
  *
- * The first time, we send the signal requested by the caller.  Then we wait
- * five seconds for the child to die, and send @SIGKILL@.  If that still
- * doesn't help, then we just give up.  It's not like there's anything else
- * we can do which is likely to help.  And it's not like the process is going
- * to be doing anything else in user mode ever again.
+ * The timeout sequencing stuff is complicated, so here's a simple machine to
+ * make it work.
  */
 
+enum {
+  TA_MOAN,                             /* Report message to user */
+#define TAARG_MOAN s
+
+  TA_KILL,                             /* Send a signal */
+#define TAARG_KILL i
+
+  TA_GOTO,                             /* Goto different state */
+#define TAARG_GOTO i
+
+  TA_WAIT,                             /* Wait for more time */
+#define TAARG_WAIT tv
+
+  TA_STATE,                            /* Alter the internal state */
+#define TAARG_STATE i
+};
+
+struct tmact {
+  unsigned act;
+  union {
+    const char *s;                     /* String parameter */
+    int i;                             /* Integer parameter */
+    struct timeval tv;                 /* Time parameter */
+  } u;
+};
+
 struct timeout {
-  sel_timer t;
-  int panic;
-  int sig;
+  sel_timer t;                         /* Timer intrusion */
+  struct tmact *ta;                    /* Instruction vector */
+  int ip;                              /* Instruction pointer */
   pid_t kid;
 };
 
 static void timeout(struct timeval *now, void *p)
 {
   struct timeout *t = p;
+  struct tmact *ta;
   struct timeval tv;
 
-  switch (t->panic) {
-    case 0:
-      moan("timed out: killing child process");
-      kill(-t->kid, t->sig);
-      break;
-    case 1:
-      moan("child hasn't responded: killing harder");
-      kill(-t->kid, SIGKILL);
-      break;
-    case 2:
-      moan("child still undead: giving up");
-      state = ST_ABORT;
-      break;
+  for (;;) {
+    ta = &t->ta[t->ip++];
+    switch (ta->act) {
+      case TA_MOAN:
+       moan(ta->u.s);
+       break;
+      case TA_KILL:
+       kill(-t->kid, ta->u.i);
+       break;
+      case TA_GOTO:
+       t->ip = ta->u.i;
+       break;
+      case TA_STATE:
+       state = ta->u.i;
+       return;
+      case TA_WAIT:
+       TV_ADD(&tv, now, &ta->u.tv);
+       sel_addtimer(&sel, &t->t, &tv, timeout, t);
+       return;
+      default:
+       moan("unexpected tmact %u", ta->act);
+       abort();
+    }
   }
-  TV_ADDL(&tv, now, 5, 0);
-  sel_addtimer(&sel, &t->t, &tv, timeout, t);
-  t->panic++;
 }
 
 /*----- Signal handling ---------------------------------------------------*/
@@ -403,10 +434,35 @@ static void sigpropagate(int sig, void *p)
 
 int main(int argc, char *const argv[])
 {
-  int signo = SIGTERM;
   pid_t kid;
-  struct timeval now, tv;
   struct timeout to;
+  struct timeval now;
+
+#define PAIR(x, y) { x, y }
+#define TACODE(I)                                                      \
+  I(sigwait,   WAIT,   PAIR(0, 0))                                     \
+  I(_a,                MOAN,   "timed out: killing child process")             \
+  I(sig,       KILL,   SIGTERM)                                        \
+  I(killwait,  WAIT,   PAIR(5, 0))                                     \
+  I(_b,                MOAN,   "child hasn't responded: killing harder")       \
+  I(_c,                KILL,   SIGKILL)                                        \
+  I(boredwait, WAIT,   PAIR(5, 0))                                     \
+  I(_d,                MOAN,   "child still undead: giving up")                \
+  I(_e,                STATE,  ST_ABORT)
+
+  enum {
+#define TALBL(label, op, arg) taoff_##label,
+    TACODE(TALBL)
+#undef TALBL
+    taoff_end
+  };
+
+  static struct tmact ta[] = {
+#define TAASM(label, op, arg) { TA_##op, { .TAARG_##op = arg } },
+    TACODE(TAASM)
+#undef TAASM
+  };
+
   struct sigchld sc;
   sig sig_CHLD;
 #define DEFSIG(tag) sig sig_##tag;
@@ -435,7 +491,7 @@ int main(int argc, char *const argv[])
       case 'v': version(stdout); exit(0);
       case 'u': usage(stdout); exit(0);
       case 's':
-       if ((signo = namesig(optarg)) < 0)
+       if ((ta[taoff_sig].u.i = namesig(optarg)) < 0)
          die(253, "bad signal spec `%s'", optarg);
        break;
       default: f |= F_BOGUS; break;
@@ -443,7 +499,7 @@ int main(int argc, char *const argv[])
   }
   argc -= optind; argv += optind;
   if ((f & F_BOGUS) || argc < 2) { usage(stderr); exit(253); }
-  strtotime(argv[0], &tv);
+  strtotime(argv[0], &ta[taoff_sigwait].u.tv);
 
   /* --- Get things set up --- */
 
@@ -476,12 +532,11 @@ int main(int argc, char *const argv[])
 
   /* --- Set up the timer --- */
 
-  to.kid = kid;
-  to.sig = signo;
-  to.panic = 0;
   gettimeofday(&now, 0);
-  TV_ADD(&tv, &now, &tv);
-  sel_addtimer(&sel, &to.t, &tv, timeout, &to);
+  to.kid = kid;
+  to.ta = ta;
+  to.ip = 0;
+  timeout(&now, &to);
 
   /* --- Main @select@ loop */