Skip to content

Commit

Permalink
chain kill signals for cleanup functions
Browse files Browse the repository at this point in the history
If a piece of code wanted to do some cleanup before exiting
(e.g., cleaning up a lockfile or a tempfile), our usual
strategy was to install a signal handler that did something
like this:

  do_cleanup(); /* actual work */
  signal(signo, SIG_DFL); /* restore previous behavior */
  raise(signo); /* deliver signal, killing ourselves */

For a single handler, this works fine. However, if we want
to clean up two _different_ things, we run into a problem.
The most recently installed handler will run, but when it
removes itself as a handler, it doesn't put back the first
handler.

This patch introduces sigchain, a tiny library for handling
a stack of signal handlers. You sigchain_push each handler,
and use sigchain_pop to restore whoever was before you in
the stack.

Signed-off-by: Jeff King <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
peff authored and gitster committed Jan 22, 2009
1 parent 479b0ae commit 4a16d07
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ test-match-trees
test-parse-options
test-path-utils
test-sha1
test-sigchain
common-cmds.h
*.tar.gz
*.dsc
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ LIB_H += revision.h
LIB_H += run-command.h
LIB_H += sha1-lookup.h
LIB_H += sideband.h
LIB_H += sigchain.h
LIB_H += strbuf.h
LIB_H += tag.h
LIB_H += transport.h
Expand Down Expand Up @@ -481,6 +482,7 @@ LIB_OBJS += sha1-lookup.o
LIB_OBJS += sha1_name.o
LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
LIB_OBJS += sigchain.o
LIB_OBJS += strbuf.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
Expand Down Expand Up @@ -1364,6 +1366,7 @@ TEST_PROGRAMS += test-match-trees$X
TEST_PROGRAMS += test-parse-options$X
TEST_PROGRAMS += test-path-utils$X
TEST_PROGRAMS += test-sha1$X
TEST_PROGRAMS += test-sigchain$X

all:: $(TEST_PROGRAMS)

Expand Down
5 changes: 3 additions & 2 deletions builtin-clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "strbuf.h"
#include "dir.h"
#include "pack-refs.h"
#include "sigchain.h"

/*
* Overall FIXMEs:
Expand Down Expand Up @@ -288,7 +289,7 @@ static void remove_junk(void)
static void remove_junk_on_signal(int signo)
{
remove_junk();
signal(SIGINT, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -438,7 +439,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
junk_git_dir = git_dir;
atexit(remove_junk);
signal(SIGINT, remove_junk_on_signal);
sigchain_push(SIGINT, remove_junk_on_signal);

setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);

Expand Down
5 changes: 3 additions & 2 deletions builtin-fetch--tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "cache.h"
#include "refs.h"
#include "commit.h"
#include "sigchain.h"

static char *get_stdin(void)
{
Expand Down Expand Up @@ -186,7 +187,7 @@ static void remove_keep(void)
static void remove_keep_on_signal(int signo)
{
remove_keep();
signal(SIGINT, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp,
char buffer[1024];
int err = 0;

signal(SIGINT, remove_keep_on_signal);
sigchain_push(SIGINT, remove_keep_on_signal);
atexit(remove_keep);

while (fgets(buffer, sizeof(buffer), stdin)) {
Expand Down
5 changes: 3 additions & 2 deletions builtin-fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "transport.h"
#include "run-command.h"
#include "parse-options.h"
#include "sigchain.h"

static const char * const builtin_fetch_usage[] = {
"git fetch [options] [<repository> <refspec>...]",
Expand Down Expand Up @@ -58,7 +59,7 @@ static void unlock_pack(void)
static void unlock_pack_on_signal(int signo)
{
unlock_pack();
signal(SIGINT, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
ref_nr = j;
}

signal(SIGINT, unlock_pack_on_signal);
sigchain_push(SIGINT, unlock_pack_on_signal);
atexit(unlock_pack);
exit_code = do_fetch(transport,
parse_fetch_refspec(ref_nr, refs), ref_nr);
Expand Down
5 changes: 3 additions & 2 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "run-command.h"
#include "utf8.h"
#include "userdiff.h"
#include "sigchain.h"

#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
Expand Down Expand Up @@ -188,7 +189,7 @@ static void remove_tempfile(void)
static void remove_tempfile_on_signal(int signo)
{
remove_tempfile();
signal(SIGINT, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -1902,7 +1903,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,

if (!remove_tempfile_installed) {
atexit(remove_tempfile);
signal(SIGINT, remove_tempfile_on_signal);
sigchain_push(SIGINT, remove_tempfile_on_signal);
remove_tempfile_installed = 1;
}

Expand Down
11 changes: 6 additions & 5 deletions http-push.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "exec_cmd.h"
#include "remote.h"
#include "list-objects.h"
#include "sigchain.h"

#include <expat.h>

Expand Down Expand Up @@ -1363,7 +1364,7 @@ static void remove_locks(void)
static void remove_locks_on_signal(int signo)
{
remove_locks();
signal(signo, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -2261,10 +2262,10 @@ int main(int argc, char **argv)
goto cleanup;
}

signal(SIGINT, remove_locks_on_signal);
signal(SIGHUP, remove_locks_on_signal);
signal(SIGQUIT, remove_locks_on_signal);
signal(SIGTERM, remove_locks_on_signal);
sigchain_push(SIGINT, remove_locks_on_signal);
sigchain_push(SIGHUP, remove_locks_on_signal);
sigchain_push(SIGQUIT, remove_locks_on_signal);
sigchain_push(SIGTERM, remove_locks_on_signal);

/* Check whether the remote has server info files */
remote->can_update_info_refs = 0;
Expand Down
13 changes: 7 additions & 6 deletions lockfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) 2005, Junio C Hamano
*/
#include "cache.h"
#include "sigchain.h"

static struct lock_file *lock_file_list;
static const char *alternate_index_output;
Expand All @@ -24,7 +25,7 @@ static void remove_lock_file(void)
static void remove_lock_file_on_signal(int signo)
{
remove_lock_file();
signal(signo, SIG_DFL);
sigchain_pop(signo);
raise(signo);
}

Expand Down Expand Up @@ -136,11 +137,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= lk->fd) {
if (!lock_file_list) {
signal(SIGINT, remove_lock_file_on_signal);
signal(SIGHUP, remove_lock_file_on_signal);
signal(SIGTERM, remove_lock_file_on_signal);
signal(SIGQUIT, remove_lock_file_on_signal);
signal(SIGPIPE, remove_lock_file_on_signal);
sigchain_push(SIGINT, remove_lock_file_on_signal);
sigchain_push(SIGHUP, remove_lock_file_on_signal);
sigchain_push(SIGTERM, remove_lock_file_on_signal);
sigchain_push(SIGQUIT, remove_lock_file_on_signal);
sigchain_push(SIGPIPE, remove_lock_file_on_signal);
atexit(remove_lock_file);
}
lk->owner = getpid();
Expand Down
43 changes: 43 additions & 0 deletions sigchain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "sigchain.h"
#include "cache.h"

#define SIGCHAIN_MAX_SIGNALS 32

struct sigchain_signal {
sigchain_fun *old;
int n;
int alloc;
};
static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];

static void check_signum(int sig)
{
if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
die("BUG: signal out of range: %d", sig);
}

int sigchain_push(int sig, sigchain_fun f)
{
struct sigchain_signal *s = signals + sig;
check_signum(sig);

ALLOC_GROW(s->old, s->n + 1, s->alloc);
s->old[s->n] = signal(sig, f);
if (s->old[s->n] == SIG_ERR)
return -1;
s->n++;
return 0;
}

int sigchain_pop(int sig)
{
struct sigchain_signal *s = signals + sig;
check_signum(sig);
if (s->n < 1)
return 0;

if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
return -1;
s->n--;
return 0;
}
9 changes: 9 additions & 0 deletions sigchain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef SIGCHAIN_H
#define SIGCHAIN_H

typedef void (*sigchain_fun)(int);

int sigchain_push(int sig, sigchain_fun f);
int sigchain_pop(int sig);

#endif /* SIGCHAIN_H */
22 changes: 22 additions & 0 deletions t/t0005-signals.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

test_description='signals work as we expect'
. ./test-lib.sh

cat >expect <<EOF
three
two
one
EOF

test_expect_success 'sigchain works' '
test-sigchain >actual
case "$?" in
130) true ;; # POSIX w/ SIGINT=2
3) true ;; # Windows
*) false ;;
esac &&
test_cmp expect actual
'

test_done
22 changes: 22 additions & 0 deletions test-sigchain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "sigchain.h"
#include "cache.h"

#define X(f) \
static void f(int sig) { \
puts(#f); \
fflush(stdout); \
sigchain_pop(sig); \
raise(sig); \
}
X(one)
X(two)
X(three)
#undef X

int main(int argc, char **argv) {
sigchain_push(SIGINT, one);
sigchain_push(SIGINT, two);
sigchain_push(SIGINT, three);
raise(SIGINT);
return 0;
}

0 comments on commit 4a16d07

Please sign in to comment.