Skip to content

Commit 4a16d07

Browse files
peffgitster
authored andcommitted
chain kill signals for cleanup functions
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]>
1 parent 479b0ae commit 4a16d07

12 files changed

+125
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ test-match-trees
152152
test-parse-options
153153
test-path-utils
154154
test-sha1
155+
test-sigchain
155156
common-cmds.h
156157
*.tar.gz
157158
*.dsc

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ LIB_H += revision.h
388388
LIB_H += run-command.h
389389
LIB_H += sha1-lookup.h
390390
LIB_H += sideband.h
391+
LIB_H += sigchain.h
391392
LIB_H += strbuf.h
392393
LIB_H += tag.h
393394
LIB_H += transport.h
@@ -481,6 +482,7 @@ LIB_OBJS += sha1-lookup.o
481482
LIB_OBJS += sha1_name.o
482483
LIB_OBJS += shallow.o
483484
LIB_OBJS += sideband.o
485+
LIB_OBJS += sigchain.o
484486
LIB_OBJS += strbuf.o
485487
LIB_OBJS += symlinks.o
486488
LIB_OBJS += tag.o
@@ -1364,6 +1366,7 @@ TEST_PROGRAMS += test-match-trees$X
13641366
TEST_PROGRAMS += test-parse-options$X
13651367
TEST_PROGRAMS += test-path-utils$X
13661368
TEST_PROGRAMS += test-sha1$X
1369+
TEST_PROGRAMS += test-sigchain$X
13671370

13681371
all:: $(TEST_PROGRAMS)
13691372

builtin-clone.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "strbuf.h"
2020
#include "dir.h"
2121
#include "pack-refs.h"
22+
#include "sigchain.h"
2223

2324
/*
2425
* Overall FIXMEs:
@@ -288,7 +289,7 @@ static void remove_junk(void)
288289
static void remove_junk_on_signal(int signo)
289290
{
290291
remove_junk();
291-
signal(SIGINT, SIG_DFL);
292+
sigchain_pop(signo);
292293
raise(signo);
293294
}
294295

@@ -438,7 +439,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
438439
}
439440
junk_git_dir = git_dir;
440441
atexit(remove_junk);
441-
signal(SIGINT, remove_junk_on_signal);
442+
sigchain_push(SIGINT, remove_junk_on_signal);
442443

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

builtin-fetch--tool.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "cache.h"
33
#include "refs.h"
44
#include "commit.h"
5+
#include "sigchain.h"
56

67
static char *get_stdin(void)
78
{
@@ -186,7 +187,7 @@ static void remove_keep(void)
186187
static void remove_keep_on_signal(int signo)
187188
{
188189
remove_keep();
189-
signal(SIGINT, SIG_DFL);
190+
sigchain_pop(signo);
190191
raise(signo);
191192
}
192193

@@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp,
245246
char buffer[1024];
246247
int err = 0;
247248

248-
signal(SIGINT, remove_keep_on_signal);
249+
sigchain_push(SIGINT, remove_keep_on_signal);
249250
atexit(remove_keep);
250251

251252
while (fgets(buffer, sizeof(buffer), stdin)) {

builtin-fetch.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "transport.h"
1111
#include "run-command.h"
1212
#include "parse-options.h"
13+
#include "sigchain.h"
1314

1415
static const char * const builtin_fetch_usage[] = {
1516
"git fetch [options] [<repository> <refspec>...]",
@@ -58,7 +59,7 @@ static void unlock_pack(void)
5859
static void unlock_pack_on_signal(int signo)
5960
{
6061
unlock_pack();
61-
signal(SIGINT, SIG_DFL);
62+
sigchain_pop(signo);
6263
raise(signo);
6364
}
6465

@@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
672673
ref_nr = j;
673674
}
674675

675-
signal(SIGINT, unlock_pack_on_signal);
676+
sigchain_push(SIGINT, unlock_pack_on_signal);
676677
atexit(unlock_pack);
677678
exit_code = do_fetch(transport,
678679
parse_fetch_refspec(ref_nr, refs), ref_nr);

diff.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "run-command.h"
1313
#include "utf8.h"
1414
#include "userdiff.h"
15+
#include "sigchain.h"
1516

1617
#ifdef NO_FAST_WORKING_DIRECTORY
1718
#define FAST_WORKING_DIRECTORY 0
@@ -188,7 +189,7 @@ static void remove_tempfile(void)
188189
static void remove_tempfile_on_signal(int signo)
189190
{
190191
remove_tempfile();
191-
signal(SIGINT, SIG_DFL);
192+
sigchain_pop(signo);
192193
raise(signo);
193194
}
194195

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

19031904
if (!remove_tempfile_installed) {
19041905
atexit(remove_tempfile);
1905-
signal(SIGINT, remove_tempfile_on_signal);
1906+
sigchain_push(SIGINT, remove_tempfile_on_signal);
19061907
remove_tempfile_installed = 1;
19071908
}
19081909

http-push.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "exec_cmd.h"
1111
#include "remote.h"
1212
#include "list-objects.h"
13+
#include "sigchain.h"
1314

1415
#include <expat.h>
1516

@@ -1363,7 +1364,7 @@ static void remove_locks(void)
13631364
static void remove_locks_on_signal(int signo)
13641365
{
13651366
remove_locks();
1366-
signal(signo, SIG_DFL);
1367+
sigchain_pop(signo);
13671368
raise(signo);
13681369
}
13691370

@@ -2261,10 +2262,10 @@ int main(int argc, char **argv)
22612262
goto cleanup;
22622263
}
22632264

2264-
signal(SIGINT, remove_locks_on_signal);
2265-
signal(SIGHUP, remove_locks_on_signal);
2266-
signal(SIGQUIT, remove_locks_on_signal);
2267-
signal(SIGTERM, remove_locks_on_signal);
2265+
sigchain_push(SIGINT, remove_locks_on_signal);
2266+
sigchain_push(SIGHUP, remove_locks_on_signal);
2267+
sigchain_push(SIGQUIT, remove_locks_on_signal);
2268+
sigchain_push(SIGTERM, remove_locks_on_signal);
22682269

22692270
/* Check whether the remote has server info files */
22702271
remote->can_update_info_refs = 0;

lockfile.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) 2005, Junio C Hamano
33
*/
44
#include "cache.h"
5+
#include "sigchain.h"
56

67
static struct lock_file *lock_file_list;
78
static const char *alternate_index_output;
@@ -24,7 +25,7 @@ static void remove_lock_file(void)
2425
static void remove_lock_file_on_signal(int signo)
2526
{
2627
remove_lock_file();
27-
signal(signo, SIG_DFL);
28+
sigchain_pop(signo);
2829
raise(signo);
2930
}
3031

@@ -136,11 +137,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
136137
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
137138
if (0 <= lk->fd) {
138139
if (!lock_file_list) {
139-
signal(SIGINT, remove_lock_file_on_signal);
140-
signal(SIGHUP, remove_lock_file_on_signal);
141-
signal(SIGTERM, remove_lock_file_on_signal);
142-
signal(SIGQUIT, remove_lock_file_on_signal);
143-
signal(SIGPIPE, remove_lock_file_on_signal);
140+
sigchain_push(SIGINT, remove_lock_file_on_signal);
141+
sigchain_push(SIGHUP, remove_lock_file_on_signal);
142+
sigchain_push(SIGTERM, remove_lock_file_on_signal);
143+
sigchain_push(SIGQUIT, remove_lock_file_on_signal);
144+
sigchain_push(SIGPIPE, remove_lock_file_on_signal);
144145
atexit(remove_lock_file);
145146
}
146147
lk->owner = getpid();

sigchain.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "sigchain.h"
2+
#include "cache.h"
3+
4+
#define SIGCHAIN_MAX_SIGNALS 32
5+
6+
struct sigchain_signal {
7+
sigchain_fun *old;
8+
int n;
9+
int alloc;
10+
};
11+
static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
12+
13+
static void check_signum(int sig)
14+
{
15+
if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
16+
die("BUG: signal out of range: %d", sig);
17+
}
18+
19+
int sigchain_push(int sig, sigchain_fun f)
20+
{
21+
struct sigchain_signal *s = signals + sig;
22+
check_signum(sig);
23+
24+
ALLOC_GROW(s->old, s->n + 1, s->alloc);
25+
s->old[s->n] = signal(sig, f);
26+
if (s->old[s->n] == SIG_ERR)
27+
return -1;
28+
s->n++;
29+
return 0;
30+
}
31+
32+
int sigchain_pop(int sig)
33+
{
34+
struct sigchain_signal *s = signals + sig;
35+
check_signum(sig);
36+
if (s->n < 1)
37+
return 0;
38+
39+
if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
40+
return -1;
41+
s->n--;
42+
return 0;
43+
}

sigchain.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef SIGCHAIN_H
2+
#define SIGCHAIN_H
3+
4+
typedef void (*sigchain_fun)(int);
5+
6+
int sigchain_push(int sig, sigchain_fun f);
7+
int sigchain_pop(int sig);
8+
9+
#endif /* SIGCHAIN_H */

t/t0005-signals.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
3+
test_description='signals work as we expect'
4+
. ./test-lib.sh
5+
6+
cat >expect <<EOF
7+
three
8+
two
9+
one
10+
EOF
11+
12+
test_expect_success 'sigchain works' '
13+
test-sigchain >actual
14+
case "$?" in
15+
130) true ;; # POSIX w/ SIGINT=2
16+
3) true ;; # Windows
17+
*) false ;;
18+
esac &&
19+
test_cmp expect actual
20+
'
21+
22+
test_done

test-sigchain.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "sigchain.h"
2+
#include "cache.h"
3+
4+
#define X(f) \
5+
static void f(int sig) { \
6+
puts(#f); \
7+
fflush(stdout); \
8+
sigchain_pop(sig); \
9+
raise(sig); \
10+
}
11+
X(one)
12+
X(two)
13+
X(three)
14+
#undef X
15+
16+
int main(int argc, char **argv) {
17+
sigchain_push(SIGINT, one);
18+
sigchain_push(SIGINT, two);
19+
sigchain_push(SIGINT, three);
20+
raise(SIGINT);
21+
return 0;
22+
}

0 commit comments

Comments
 (0)