Skip to content

Commit

Permalink
Put a time limit on memcached processes started from within tests.
Browse files Browse the repository at this point in the history
This solves the problem where certain test failures would cause
indefinite hangs as child processes refused to ever exit.
  • Loading branch information
dustin committed Aug 15, 2009
1 parent 50d7188 commit fba0a89
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ doc/protocol-binary.txt
/version.m4 /version.m4
/version.num /version.num
/testapp /testapp
/timedrun
/doc/doxy /doc/doxy
4 changes: 3 additions & 1 deletion Makefile.am
Original file line number Original file line Diff line number Diff line change
@@ -1,11 +1,13 @@
bin_PROGRAMS = memcached bin_PROGRAMS = memcached
pkginclude_HEADERS = protocol_binary.h pkginclude_HEADERS = protocol_binary.h
noinst_PROGRAMS = memcached-debug sizes testapp noinst_PROGRAMS = memcached-debug sizes testapp timedrun


BUILT_SOURCES= BUILT_SOURCES=


testapp_SOURCES = testapp.c util.c util.h testapp_SOURCES = testapp.c util.c util.h


timedrun_SOURCES = timedrun.c

memcached_SOURCES = memcached.c memcached.h \ memcached_SOURCES = memcached.c memcached.h \
hash.c hash.h \ hash.c hash.h \
slabs.c slabs.h \ slabs.c slabs.h \
Expand Down
2 changes: 1 addition & 1 deletion t/lib/MemcachedTest.pm
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ sub new_memcached {
croak("memcached binary not executable\n") unless -x _; croak("memcached binary not executable\n") unless -x _;


unless ($childpid) { unless ($childpid) {
exec "$exe $args"; exec "$builddir/timedrun 600 $exe $args";
exit; # never gets here. exit; # never gets here.
} }


Expand Down
9 changes: 7 additions & 2 deletions testapp.c
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -263,9 +263,13 @@ static pid_t start_server(in_port_t *port_out, bool daemon) {


if (pid == 0) { if (pid == 0) {
/* Child */ /* Child */
char *argv[10]; char *argv[20];
int arg = 0; int arg = 0;
putenv(environment); putenv(environment);
if (!daemon) {
argv[arg++] = "./timedrun";
argv[arg++] = "15";
}
argv[arg++] = "./memcached-debug"; argv[arg++] = "./memcached-debug";
argv[arg++] = "-p"; argv[arg++] = "-p";
argv[arg++] = "-1"; argv[arg++] = "-1";
Expand All @@ -277,7 +281,7 @@ static pid_t start_server(in_port_t *port_out, bool daemon) {
argv[arg++] = pid_file; argv[arg++] = pid_file;
} }
argv[arg++] = NULL; 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 ;-) */ /* Yeah just let us "busy-wait" for the file to be created ;-) */
Expand Down Expand Up @@ -447,6 +451,7 @@ int main(int argc, char **argv)


for (ii = 0; testcases[ii].description != NULL; ++ii) { for (ii = 0; testcases[ii].description != NULL; ++ii) {
fflush(stdout); fflush(stdout);
alarm(60);
enum test_return ret = testcases[ii].function(); enum test_return ret = testcases[ii].function();
if (ret == TEST_SKIP) { if (ret == TEST_SKIP) {
fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description); fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
Expand Down
101 changes: 101 additions & 0 deletions timedrun.c
Original file line number Original file line Diff line number Diff line change
@@ -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.