Skip to content

Commit 6ac6f87

Browse files
j6tgitster
authored andcommitted
Windows: Work around intermittent failures in mingw_rename
We have replaced rename() with a version that can rename a file to a destination that already exists. Nevertheless, many users, the author included, observe failures in the code that are not reproducible. The theory is that the failures are due to some other process that happens to have opened the destination file briefly at the wrong moment. (And there is no way on Windows to delete or replace a file that is currently open.) The most likely candidate for such a process is a virus scanner. The failure is more often observed while there is heavy git activity (for example while the test suite is running or during a rebase operation). We work around the failure by retrying the rename operation if it failed due to ERROR_ACCESS_DENIED. The retries are delayed a bit: The first only by giving up the time slice, the next after the minimal scheduling granularity, and if more retries are needed, then we wait some non-trivial amount of time with exponential back-off. Signed-off-by: Johannes Sixt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ccb4b53 commit 6ac6f87

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

compat/mingw.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,9 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
934934
#undef rename
935935
int mingw_rename(const char *pold, const char *pnew)
936936
{
937-
DWORD attrs;
937+
DWORD attrs, gle;
938+
int tries = 0;
939+
static const int delay[] = { 0, 1, 10, 20, 40 };
938940

939941
/*
940942
* Try native rename() first to get errno right.
@@ -944,10 +946,12 @@ int mingw_rename(const char *pold, const char *pnew)
944946
return 0;
945947
if (errno != EEXIST)
946948
return -1;
949+
repeat:
947950
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
948951
return 0;
949952
/* TODO: translate more errors */
950-
if (GetLastError() == ERROR_ACCESS_DENIED &&
953+
gle = GetLastError();
954+
if (gle == ERROR_ACCESS_DENIED &&
951955
(attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
952956
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
953957
errno = EISDIR;
@@ -957,10 +961,23 @@ int mingw_rename(const char *pold, const char *pnew)
957961
SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
958962
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
959963
return 0;
964+
gle = GetLastError();
960965
/* revert file attributes on failure */
961966
SetFileAttributes(pnew, attrs);
962967
}
963968
}
969+
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
970+
/*
971+
* We assume that some other process had the source or
972+
* destination file open at the wrong moment and retry.
973+
* In order to give the other process a higher chance to
974+
* complete its operation, we give up our time slice now.
975+
* If we have to retry again, we do sleep a bit.
976+
*/
977+
Sleep(delay[tries]);
978+
tries++;
979+
goto repeat;
980+
}
964981
errno = EACCES;
965982
return -1;
966983
}

0 commit comments

Comments
 (0)