summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 479b0ae)
raw | patch | inline | side by side (parent: 479b0ae)
author | Jeff King <peff@peff.net> | |
Thu, 22 Jan 2009 06:02:35 +0000 (01:02 -0500) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Thu, 22 Jan 2009 06:46:52 +0000 (22:46 -0800) |
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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
12 files changed:
.gitignore | patch | blob | history | |
Makefile | patch | blob | history | |
builtin-clone.c | patch | blob | history | |
builtin-fetch--tool.c | patch | blob | history | |
builtin-fetch.c | patch | blob | history | |
diff.c | patch | blob | history | |
http-push.c | patch | blob | history | |
lockfile.c | patch | blob | history | |
sigchain.c | [new file with mode: 0644] | patch | blob |
sigchain.h | [new file with mode: 0644] | patch | blob |
t/t0005-signals.sh | [new file with mode: 0755] | patch | blob |
test-sigchain.c | [new file with mode: 0644] | patch | blob |
diff --git a/.gitignore b/.gitignore
index d9adce585af99e617a2906a89029a92045a79538..f28a54d262e77f9bd457cd26f83dd78632fc3633 100644 (file)
--- a/.gitignore
+++ b/.gitignore
test-parse-options
test-path-utils
test-sha1
+test-sigchain
common-cmds.h
*.tar.gz
*.dsc
diff --git a/Makefile b/Makefile
index 2b873fa99f8ddc70c7560fd64a516a051afc3a7a..fd02decc01a053ad31fdaac66fde86033c0f2bfb 100644 (file)
--- a/Makefile
+++ b/Makefile
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
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
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)
diff --git a/builtin-clone.c b/builtin-clone.c
index f1a1a0c36591d1936088a180e3c0a8ca5e7b0757..18b9392334a0931076855b175ab368deea8ac122 100644 (file)
--- a/builtin-clone.c
+++ b/builtin-clone.c
#include "strbuf.h"
#include "dir.h"
#include "pack-refs.h"
+#include "sigchain.h"
/*
* Overall FIXMEs:
static void remove_junk_on_signal(int signo)
{
remove_junk();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
}
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);
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index 469b07e240953aa21fd67eb2563e094a7f0f3d42..b1d7f8fb32fd2d2ee6cfd18ed310a5d70e9e5ceb 100644 (file)
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
#include "cache.h"
#include "refs.h"
#include "commit.h"
+#include "sigchain.h"
static char *get_stdin(void)
{
static void remove_keep_on_signal(int signo)
{
remove_keep();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
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)) {
diff --git a/builtin-fetch.c b/builtin-fetch.c
index de6f3074b1121fdbcbe8bf0593dee446a17fb08e..8c86974cbee0341d7c0e110055bb4347ad5908ad 100644 (file)
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
#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>...]",
static void unlock_pack_on_signal(int signo)
{
unlock_pack();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
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);
index 3cfc0b636c83d5012a138131db964c198a1a6c33..9c9977d892e475b0fa98a946393118ef59d73100 100644 (file)
--- a/diff.c
+++ b/diff.c
#include "run-command.h"
#include "utf8.h"
#include "userdiff.h"
+#include "sigchain.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
static void remove_tempfile_on_signal(int signo)
{
remove_tempfile();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
if (!remove_tempfile_installed) {
atexit(remove_tempfile);
- signal(SIGINT, remove_tempfile_on_signal);
+ sigchain_push(SIGINT, remove_tempfile_on_signal);
remove_tempfile_installed = 1;
}
diff --git a/http-push.c b/http-push.c
index a4b7d08663504a57008f66a39fffe293f62c1d08..dec395deed0778b707b62e86a35086f6e6b73a72 100644 (file)
--- a/http-push.c
+++ b/http-push.c
#include "exec_cmd.h"
#include "remote.h"
#include "list-objects.h"
+#include "sigchain.h"
#include <expat.h>
static void remove_locks_on_signal(int signo)
{
remove_locks();
- signal(signo, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
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;
diff --git a/lockfile.c b/lockfile.c
index 8589155532da9eb7f42a1e9c3132fcf42b1b9275..3cd57dc3854c22b071619e03c3ccd26981b0c45b 100644 (file)
--- a/lockfile.c
+++ b/lockfile.c
* 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;
static void remove_lock_file_on_signal(int signo)
{
remove_lock_file();
- signal(signo, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
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();
diff --git a/sigchain.c b/sigchain.c
--- /dev/null
+++ b/sigchain.c
@@ -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;
+}
diff --git a/sigchain.h b/sigchain.h
--- /dev/null
+++ b/sigchain.h
@@ -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 */
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
--- /dev/null
+++ b/t/t0005-signals.sh
@@ -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
diff --git a/test-sigchain.c b/test-sigchain.c
--- /dev/null
+++ b/test-sigchain.c
@@ -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;
+}