Skip to content

Commit c45af94

Browse files
peffgitster
authored andcommitted
gc: run pre-detach operations under lock
We normally try to avoid having two auto-gc operations run at the same time, because it wastes resources. This was done long ago in 64a99eb (gc: reject if another gc is running, unless --force is given, 2013-08-08). When we do a detached auto-gc, we run the ref-related commands _before_ detaching, to avoid confusing lock contention. This was done by 62aad18 (gc --auto: do not lock refs in the background, 2014-05-25). These two features do not interact well. The pre-detach operations are run before we check the gc.pid lock, meaning that on a busy repository we may run many of them concurrently. Ideally we'd take the lock before spawning any operations, and hold it for the duration of the program. This is tricky, though, with the way the pid-file interacts with the daemonize() process. Other processes will check that the pid recorded in the pid-file still exists. But detaching causes us to fork and continue running under a new pid. So if we take the lock before detaching, the pid-file will have a bogus pid in it. We'd have to go back and update it with the new pid after detaching. We'd also have to play some tricks with the tempfile subsystem to tweak the "owner" field, so that the parent process does not clean it up on exit, but the child process does. Instead, we can do something a bit simpler: take the lock only for the duration of the pre-detach work, then detach, then take it again for the post-detach work. Technically, this means that the post-detach lock could lose to another process doing pre-detach work. But in the long run this works out. That second process would then follow-up by doing post-detach work. Unless it was in turn blocked by a third process doing pre-detach work, and so on. This could in theory go on indefinitely, as the pre-detach work does not repack, and so need_to_gc() will continue to trigger. But in each round we are racing between the pre- and post-detach locks. Eventually, one of the post-detach locks will win the race and complete the full gc. So in the worst case, we may racily repeat the pre-detach work, but we would never do so simultaneously (it would happen via a sequence of serialized race-wins). Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 699d47e commit c45af94

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

builtin/gc.c

+4
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
413413
if (report_last_gc_error())
414414
return -1;
415415

416+
if (lock_repo_for_gc(force, &pid))
417+
return 0;
416418
if (gc_before_repack())
417419
return -1;
420+
delete_tempfile(&pidfile);
421+
418422
/*
419423
* failure to daemonize is ok, we'll continue
420424
* in foreground

t/t6500-gc.sh

+21
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
9595
test_line_count = 1 packs
9696
'
9797

98+
test_expect_success 'background auto gc respects lock for all operations' '
99+
# make sure we run a background auto-gc
100+
test_commit make-pack &&
101+
git repack &&
102+
test_config gc.autopacklimit 1 &&
103+
test_config gc.autodetach true &&
104+
105+
# create a ref whose loose presence we can use to detect a pack-refs run
106+
git update-ref refs/heads/should-be-loose HEAD &&
107+
test_path_is_file .git/refs/heads/should-be-loose &&
108+
109+
# now fake a concurrent gc that holds the lock; we can use our
110+
# shell pid so that it looks valid.
111+
hostname=$(hostname || echo unknown) &&
112+
printf "$$ %s" "$hostname" >.git/gc.pid &&
113+
114+
# our gc should exit zero without doing anything
115+
run_and_wait_for_auto_gc &&
116+
test_path_is_file .git/refs/heads/should-be-loose
117+
'
118+
98119
# DO NOT leave a detached auto gc process running near the end of the
99120
# test script: it can run long enough in the background to racily
100121
# interfere with the cleanup in 'test_done'.

0 commit comments

Comments
 (0)