Commit 2873a862fd1899909424b60b44fa0680851a60f6

Jan Melcher 2014-03-27T12:42:44

Retry renaming files on Access Denied errors When a file is open for reading (without shared-delete permission), and then a different thread/process called p_rename, that would fail, even if the file was only open for reading for a few milliseconds. This change lets p_rename wait up to 50ms for the file to be closed by the reader. Applies only to win32. This is especially important for git_filebuf_commit, because writes should not fail if the file is read simultaneously. Fixes #2207

diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 18f717b..6f29318 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -467,10 +467,31 @@ int p_rename(const char *from, const char *to)
 {
 	git_win32_path wfrom;
 	git_win32_path wto;
+	int rename_tries;
+	int rename_succeeded;
+	int error;
 
 	git_win32_path_from_c(wfrom, from);
 	git_win32_path_from_c(wto, to);
-	return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
+	
+	/* wait up to 50ms if file is locked by another thread or process */
+	rename_tries = 0;
+	rename_succeeded = 0;
+	while (rename_tries < 10) {
+		if (MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
+			rename_succeeded = 1;
+			break;
+		}
+		
+		error = GetLastError();
+		if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
+			Sleep(5);
+			rename_tries++;
+		} else
+			break;
+	}
+	
+	return rename_succeeded ? 0 : -1;
 }
 
 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)