Merge branch 'js/maintenance-fix-deadlock-on-win10'
To help Windows 10 installations, avoid removing files whose contents are still mmap()'ed. * js/maintenance-fix-deadlock-on-win10: maintenance(geometric): do release the `.idx` files before repacking mingw: optionally use legacy (non-POSIX) delete semanticsmain
commit
d14a9794dd
|
|
@ -1590,6 +1590,7 @@ static int maintenance_task_geometric_repack(struct maintenance_run_opts *opts,
|
||||||
pack_geometry_split(&geometry);
|
pack_geometry_split(&geometry);
|
||||||
|
|
||||||
child.git_cmd = 1;
|
child.git_cmd = 1;
|
||||||
|
child.odb_to_close = the_repository->objects;
|
||||||
|
|
||||||
strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
|
strvec_pushl(&child.args, "repack", "-d", "-l", NULL);
|
||||||
if (geometry.split < geometry.pack_nr)
|
if (geometry.split < geometry.pack_nr)
|
||||||
|
|
|
||||||
|
|
@ -449,20 +449,63 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
||||||
return wbuf;
|
return wbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use SetFileInformationByHandle(FileDispositionInfo) to force legacy
|
||||||
|
* (non-POSIX) delete semantics. On Windows 11, DeleteFileW() uses POSIX
|
||||||
|
* delete semantics internally, allowing deletion even with active
|
||||||
|
* MapViewOfFile views. This helper simulates Windows 10 behavior where
|
||||||
|
* deletion fails if a file mapping exists.
|
||||||
|
*
|
||||||
|
* Returns nonzero on success (like DeleteFileW), 0 on failure.
|
||||||
|
*/
|
||||||
|
static int legacy_delete_file(const wchar_t *wpathname)
|
||||||
|
{
|
||||||
|
FILE_DISPOSITION_INFO fdi = { TRUE };
|
||||||
|
DWORD gle;
|
||||||
|
HANDLE h = CreateFileW(wpathname, DELETE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE |
|
||||||
|
FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (SetFileInformationByHandle(h, FileDispositionInfo,
|
||||||
|
&fdi, sizeof(fdi))) {
|
||||||
|
CloseHandle(h);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
gle = GetLastError();
|
||||||
|
CloseHandle(h);
|
||||||
|
SetLastError(gle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int try_delete_file(const wchar_t *wpathname, int use_legacy)
|
||||||
|
{
|
||||||
|
if (use_legacy)
|
||||||
|
return legacy_delete_file(wpathname);
|
||||||
|
return DeleteFileW(wpathname);
|
||||||
|
}
|
||||||
|
|
||||||
int mingw_unlink(const char *pathname, int handle_in_use_error)
|
int mingw_unlink(const char *pathname, int handle_in_use_error)
|
||||||
{
|
{
|
||||||
|
static int use_legacy_delete = -1;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
wchar_t wpathname[MAX_PATH];
|
wchar_t wpathname[MAX_PATH];
|
||||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (DeleteFileW(wpathname))
|
if (use_legacy_delete < 0)
|
||||||
|
use_legacy_delete = git_env_bool("GIT_TEST_LEGACY_DELETE", 0);
|
||||||
|
|
||||||
|
if (try_delete_file(wpathname, use_legacy_delete))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* read-only files cannot be removed */
|
/* read-only files cannot be removed */
|
||||||
_wchmod(wpathname, 0666);
|
_wchmod(wpathname, 0666);
|
||||||
if (!_wunlink(wpathname))
|
if (try_delete_file(wpathname, use_legacy_delete))
|
||||||
return 0;
|
return 0;
|
||||||
if (!is_file_in_use_error(GetLastError()))
|
if (!is_file_in_use_error(GetLastError()))
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,16 @@ run_and_verify_geometric_pack () {
|
||||||
|
|
||||||
# And verify that there are no loose objects anymore.
|
# And verify that there are no loose objects anymore.
|
||||||
git count-objects -v >count &&
|
git count-objects -v >count &&
|
||||||
test_grep '^count: 0$' count
|
test_grep '^count: 0$' count &&
|
||||||
|
|
||||||
|
# Verify that no orphaned .idx files were left behind. On
|
||||||
|
# Windows, a missing odb_to_close causes the parent to hold
|
||||||
|
# mmap handles on .idx files, silently preventing their
|
||||||
|
# deletion by the child git-repack process.
|
||||||
|
ls .git/objects/pack/pack-*.idx .git/objects/pack/pack-*.pack |
|
||||||
|
sed "s/\.pack$/.idx/" |
|
||||||
|
sort | uniq -u >orphaned-idx &&
|
||||||
|
test_must_be_empty orphaned-idx
|
||||||
}
|
}
|
||||||
|
|
||||||
test_expect_success 'geometric repacking task' '
|
test_expect_success 'geometric repacking task' '
|
||||||
|
|
@ -580,8 +589,19 @@ test_expect_success 'geometric repacking task' '
|
||||||
|
|
||||||
# And these two small packs should now be merged via the
|
# And these two small packs should now be merged via the
|
||||||
# geometric repack. The large packfile should remain intact.
|
# geometric repack. The large packfile should remain intact.
|
||||||
|
cp -R .git/objects .git/objects.save &&
|
||||||
run_and_verify_geometric_pack 2 &&
|
run_and_verify_geometric_pack 2 &&
|
||||||
|
|
||||||
|
# On Windows, verify the same with legacy delete semantics
|
||||||
|
# that reject deletion of mmap-held .idx files.
|
||||||
|
if test_have_prereq MINGW
|
||||||
|
then
|
||||||
|
rm -rf .git/objects &&
|
||||||
|
mv .git/objects.save .git/objects &&
|
||||||
|
test_env GIT_TEST_LEGACY_DELETE=1 \
|
||||||
|
run_and_verify_geometric_pack 2
|
||||||
|
fi &&
|
||||||
|
|
||||||
# If we now add two more objects and repack twice we should
|
# If we now add two more objects and repack twice we should
|
||||||
# then see another all-into-one repack. This time around
|
# then see another all-into-one repack. This time around
|
||||||
# though, as we have unreachable objects, we should also see a
|
# though, as we have unreachable objects, we should also see a
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue