From: Mark Wooding Date: Thu, 15 Dec 2011 00:55:01 +0000 (+0000) Subject: mtimeout.c: Restructure timeout handling to use explicit machine. X-Git-Tag: 1.2.7~7 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/misc/commitdiff_plain/f42ff973cd0e180e7decb57cd9818a3f18e635f7 mtimeout.c: Restructure timeout handling to use explicit machine. 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. --- diff --git a/mtimeout.c b/mtimeout.c index 38723ae..055ef7e 100644 --- a/mtimeout.c +++ b/mtimeout.c @@ -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 */