Skip to content

Commit 1b5074e

Browse files
pks-tgitster
authored andcommitted
builtin/maintenance: fix locking race when handling "gc" task
The "gc" task has a similar locking race as the one that we have fixed for the "pack-refs" and "reflog-expire" tasks in preceding commits. Fix this by splitting up the logic of the "gc" task: - We execute `gc_before_repack()` in the foreground, which contains the logic that git-gc(1) itself would execute in the foreground, as well. - We spawn git-gc(1) after detaching, but with a new hidden flag that suppresses calling `gc_before_repack()`. Like this we have roughly the same logic as git-gc(1) itself and know to repack refs and reflogs before detaching, thus fixing the race. Note that `gc_before_repack()` is renamed to `gc_foreground_tasks()` to better reflect what this function does. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d2b084c commit 1b5074e

File tree

2 files changed

+33
-20
lines changed

2 files changed

+33
-20
lines changed

builtin/gc.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -816,8 +816,8 @@ static int report_last_gc_error(void)
816816
return ret;
817817
}
818818

819-
static int gc_before_repack(struct maintenance_run_opts *opts,
820-
struct gc_config *cfg)
819+
static int gc_foreground_tasks(struct maintenance_run_opts *opts,
820+
struct gc_config *cfg)
821821
{
822822
if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
823823
return error(FAILED_RUN, "pack-refs");
@@ -837,6 +837,7 @@ int cmd_gc(int argc,
837837
pid_t pid;
838838
int daemonized = 0;
839839
int keep_largest_pack = -1;
840+
int skip_foreground_tasks = 0;
840841
timestamp_t dummy;
841842
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
842843
struct gc_config cfg = GC_CONFIG_INIT;
@@ -869,6 +870,8 @@ int cmd_gc(int argc,
869870
N_("repack all other packs except the largest pack")),
870871
OPT_STRING(0, "expire-to", &cfg.repack_expire_to, N_("dir"),
871872
N_("pack prefix to store a pack containing pruned objects")),
873+
OPT_HIDDEN_BOOL(0, "skip-foreground-tasks", &skip_foreground_tasks,
874+
N_("skip maintenance tasks typically done in the foreground")),
872875
OPT_END()
873876
};
874877

@@ -952,14 +955,16 @@ int cmd_gc(int argc,
952955
goto out;
953956
}
954957

955-
if (lock_repo_for_gc(force, &pid)) {
956-
ret = 0;
957-
goto out;
958-
}
958+
if (!skip_foreground_tasks) {
959+
if (lock_repo_for_gc(force, &pid)) {
960+
ret = 0;
961+
goto out;
962+
}
959963

960-
if (gc_before_repack(&opts, &cfg) < 0)
961-
die(NULL);
962-
delete_tempfile(&pidfile);
964+
if (gc_foreground_tasks(&opts, &cfg) < 0)
965+
die(NULL);
966+
delete_tempfile(&pidfile);
967+
}
963968

964969
/*
965970
* failure to daemonize is ok, we'll continue
@@ -988,8 +993,8 @@ int cmd_gc(int argc,
988993
free(path);
989994
}
990995

991-
if (opts.detach <= 0)
992-
gc_before_repack(&opts, &cfg);
996+
if (opts.detach <= 0 && !skip_foreground_tasks)
997+
gc_foreground_tasks(&opts, &cfg);
993998

994999
if (!repository_format_precious_objects) {
9951000
struct child_process repack_cmd = CHILD_PROCESS_INIT;
@@ -1225,8 +1230,14 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts,
12251230
return 0;
12261231
}
12271232

1228-
static int maintenance_task_gc(struct maintenance_run_opts *opts,
1229-
struct gc_config *cfg UNUSED)
1233+
static int maintenance_task_gc_foreground(struct maintenance_run_opts *opts,
1234+
struct gc_config *cfg)
1235+
{
1236+
return gc_foreground_tasks(opts, cfg);
1237+
}
1238+
1239+
static int maintenance_task_gc_background(struct maintenance_run_opts *opts,
1240+
struct gc_config *cfg UNUSED)
12301241
{
12311242
struct child_process child = CHILD_PROCESS_INIT;
12321243

@@ -1240,6 +1251,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts,
12401251
else
12411252
strvec_push(&child.args, "--no-quiet");
12421253
strvec_push(&child.args, "--no-detach");
1254+
strvec_push(&child.args, "--skip-foreground-tasks");
12431255

12441256
return run_command(&child);
12451257
}
@@ -1571,7 +1583,8 @@ static const struct maintenance_task tasks[] = {
15711583
},
15721584
[TASK_GC] = {
15731585
.name = "gc",
1574-
.background = maintenance_task_gc,
1586+
.foreground = maintenance_task_gc_foreground,
1587+
.background = maintenance_task_gc_background,
15751588
.auto_condition = need_to_gc,
15761589
},
15771590
[TASK_COMMIT_GRAPH] = {

t/t7900-maintenance.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ test_expect_success 'run [--auto|--quiet]' '
4949
git maintenance run --auto 2>/dev/null &&
5050
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
5151
git maintenance run --no-quiet 2>/dev/null &&
52-
test_subcommand git gc --quiet --no-detach <run-no-auto.txt &&
53-
test_subcommand ! git gc --auto --quiet --no-detach <run-auto.txt &&
54-
test_subcommand git gc --no-quiet --no-detach <run-no-quiet.txt
52+
test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-no-auto.txt &&
53+
test_subcommand ! git gc --auto --quiet --no-detach --skip-foreground-tasks <run-auto.txt &&
54+
test_subcommand git gc --no-quiet --no-detach --skip-foreground-tasks <run-no-quiet.txt
5555
'
5656

5757
test_expect_success 'maintenance.auto config option' '
@@ -154,9 +154,9 @@ test_expect_success 'run --task=<task>' '
154154
git maintenance run --task=commit-graph 2>/dev/null &&
155155
GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
156156
git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
157-
test_subcommand ! git gc --quiet --no-detach <run-commit-graph.txt &&
158-
test_subcommand git gc --quiet --no-detach <run-gc.txt &&
159-
test_subcommand git gc --quiet --no-detach <run-both.txt &&
157+
test_subcommand ! git gc --quiet --no-detach --skip-foreground-tasks <run-commit-graph.txt &&
158+
test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-gc.txt &&
159+
test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-both.txt &&
160160
test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
161161
test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
162162
test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt

0 commit comments

Comments
 (0)