Skip to content
Browse files

Put a time limit on memcached processes started from within tests.

This solves the problem where certain test failures would cause
indefinite hangs as child processes refused to ever exit.
  • Loading branch information...
1 parent 50d7188 commit fba0a8919dd24c9dafa9459e4b046a58649392e0 @dustin dustin committed
Showing with 113 additions and 4 deletions.
  1. +1 −0 .gitignore
  2. +3 −1 Makefile.am
  3. +1 −1 t/lib/MemcachedTest.pm
  4. +7 −2 testapp.c
  5. +101 −0 timedrun.c
View
1 .gitignore
@@ -36,4 +36,5 @@ doc/protocol-binary.txt
/version.m4
/version.num
/testapp
+/timedrun
/doc/doxy
View
4 Makefile.am
@@ -1,11 +1,13 @@
bin_PROGRAMS = memcached
pkginclude_HEADERS = protocol_binary.h
-noinst_PROGRAMS = memcached-debug sizes testapp
+noinst_PROGRAMS = memcached-debug sizes testapp timedrun
BUILT_SOURCES=
testapp_SOURCES = testapp.c util.c util.h
+timedrun_SOURCES = timedrun.c
+
memcached_SOURCES = memcached.c memcached.h \
hash.c hash.h \
slabs.c slabs.h \
View
2 t/lib/MemcachedTest.pm
@@ -159,7 +159,7 @@ sub new_memcached {
croak("memcached binary not executable\n") unless -x _;
unless ($childpid) {
- exec "$exe $args";
+ exec "$builddir/timedrun 600 $exe $args";
exit; # never gets here.
}
View
9 testapp.c
@@ -263,9 +263,13 @@ static pid_t start_server(in_port_t *port_out, bool daemon) {
if (pid == 0) {
/* Child */
- char *argv[10];
+ char *argv[20];
int arg = 0;
putenv(environment);
+ if (!daemon) {
+ argv[arg++] = "./timedrun";
+ argv[arg++] = "15";
+ }
argv[arg++] = "./memcached-debug";
argv[arg++] = "-p";
argv[arg++] = "-1";
@@ -277,7 +281,7 @@ static pid_t start_server(in_port_t *port_out, bool daemon) {
argv[arg++] = pid_file;
}
argv[arg++] = NULL;
- assert(execv("./memcached-debug", argv) != -1);
+ assert(execv(argv[0], argv) != -1);
}
/* Yeah just let us "busy-wait" for the file to be created ;-) */
@@ -447,6 +451,7 @@ int main(int argc, char **argv)
for (ii = 0; testcases[ii].description != NULL; ++ii) {
fflush(stdout);
+ alarm(60);
enum test_return ret = testcases[ii].function();
if (ret == TEST_SKIP) {
fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
View
101 timedrun.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sysexits.h>
+
+#include <assert.h>
+
+static int caught = 0;
+
+static void caught_signal(int which)
+{
+ caught = which;
+}
+
+static int wait_for_process(pid_t pid)
+{
+ int rv = EX_SOFTWARE;
+ int stats = 0;
+ int i = 0;
+ struct sigaction sig_handler;
+
+ sig_handler.sa_handler = caught_signal;
+ sig_handler.sa_flags = 0;
+
+ sigaction(SIGALRM, &sig_handler, NULL);
+ sigaction(SIGHUP, &sig_handler, NULL);
+ sigaction(SIGINT, &sig_handler, NULL);
+ sigaction(SIGTERM, &sig_handler, NULL);
+ sigaction(SIGPIPE, &sig_handler, NULL);
+
+ /* Loop forever waiting for the process to quit */
+ for (i = 0; ;i++) {
+ pid_t p = waitpid(pid, &stats, 0);
+ if (p == pid) {
+ /* child exited. Let's get out of here */
+ rv = WIFEXITED(stats) ?
+ WEXITSTATUS(stats) :
+ (0x80 | WTERMSIG(stats));
+ break;
+ } else {
+ int sig = 0;
+ switch (i) {
+ case 0:
+ /* On the first iteration, pass the signal through */
+ sig = caught > 0 ? caught : SIGTERM;
+ break;
+ case 1:
+ sig = SIGTERM;
+ break;
+ default:
+ sig = SIGKILL;
+ break;
+ }
+ if (kill(pid, sig) < 0) {
+ /* Kill failed. Must have lost the process. :/ */
+ perror("lost child when trying to kill");
+ }
+ /* Wait up to 5 seconds for the pid */
+ alarm(5);
+ }
+ }
+ return rv;
+}
+
+static int spawn_and_wait(int argc, char **argv)
+{
+ int rv = EX_SOFTWARE;
+ pid_t pid = fork();
+
+ assert(argc > 1);
+
+ switch (pid) {
+ case -1:
+ perror("fork");
+ rv = EX_OSERR;
+ break; /* NOTREACHED */
+ case 0:
+ execvp(argv[0], argv);
+ perror("exec");
+ rv = EX_SOFTWARE;
+ break; /* NOTREACHED */
+ default:
+ rv = wait_for_process(pid);
+ }
+ return rv;
+}
+
+int main(int argc, char **argv)
+{
+ int naptime = 0;
+ assert(argc > 2);
+
+ naptime = atoi(argv[1]);
+ assert(naptime > 0 && naptime < 1800);
+
+ alarm(naptime);
+
+ return spawn_and_wait(argc+2, argv+2);
+}

0 comments on commit fba0a89

Please sign in to comment.
Something went wrong with that request. Please try again.