Skip to content

Commit ab16b9a

Browse files
Mic92gitster
authored andcommitted
config: retry acquiring config.lock for 100ms
When multiple processes write to a config file concurrently, they contend on its ".lock" file, which is acquired via open(O_EXCL) with no retry. The losers fail immediately with "could not lock config file". Two processes writing unrelated keys (say, "branch.a.remote" and "branch.b.remote") have no semantic conflict, yet one of them fails for a purely mechanical reason. This bites in practice when running `git worktree add -b` concurrently against the same repository. Each invocation makes several writes to ".git/config" to set up branch tracking, and tooling that creates worktrees in parallel sees intermittent failures. Worse, `git worktree add` does not propagate the failed config write to its exit code: the worktree is created and the command exits 0, but tracking configuration is silently dropped. The lock is held only for the duration of rewriting a small file, so retrying for 100 ms papers over any realistic contention while still failing fast if a stale lock has been left behind by a crashed process. This mirrors what we already do for individual reference locks (4ff0f01 (refs: retry acquiring reference locks for 100ms, 2017-08-21)). Signed-off-by: Jörg Thalheim <joerg@thalheim.io> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 2565546 commit ab16b9a

File tree

1 file changed

+12
-2
lines changed

1 file changed

+12
-2
lines changed

config.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,6 +2903,14 @@ char *git_config_prepare_comment_string(const char *comment)
29032903
return prepared;
29042904
}
29052905

2906+
/*
2907+
* How long to retry acquiring config.lock when another process holds it.
2908+
* The lock is held only for the duration of rewriting a small file, so
2909+
* 100 ms covers any realistic contention while still failing fast if
2910+
* a stale lock has been left behind by a crashed process.
2911+
*/
2912+
#define CONFIG_LOCK_TIMEOUT_MS 100
2913+
29062914
static void validate_comment_string(const char *comment)
29072915
{
29082916
size_t leading_blanks;
@@ -2986,7 +2994,8 @@ int repo_config_set_multivar_in_file_gently(struct repository *r,
29862994
* The lock serves a purpose in addition to locking: the new
29872995
* contents of .git/config will be written into it.
29882996
*/
2989-
fd = hold_lock_file_for_update(&lock, config_filename, 0);
2997+
fd = hold_lock_file_for_update_timeout(&lock, config_filename, 0,
2998+
CONFIG_LOCK_TIMEOUT_MS);
29902999
if (fd < 0) {
29913000
error_errno(_("could not lock config file %s"), config_filename);
29923001
ret = CONFIG_NO_LOCK;
@@ -3331,7 +3340,8 @@ static int repo_config_copy_or_rename_section_in_file(
33313340
if (!config_filename)
33323341
config_filename = filename_buf = repo_git_path(r, "config");
33333342

3334-
out_fd = hold_lock_file_for_update(&lock, config_filename, 0);
3343+
out_fd = hold_lock_file_for_update_timeout(&lock, config_filename, 0,
3344+
CONFIG_LOCK_TIMEOUT_MS);
33353345
if (out_fd < 0) {
33363346
ret = error(_("could not lock config file %s"), config_filename);
33373347
goto out;

0 commit comments

Comments
 (0)