mirror of
https://github.com/git/git.git
synced 2024-06-13 11:56:20 +02:00
Win32: factor out retry logic
The retry pattern is duplicated in three places. It also seems to be too hard to use: mingw_unlink() and mingw_rmdir() duplicate the code to retry, and both of them do so incompletely. They also do not restore errno if the user answers 'no'. Introduce a retry_ask_yes_no() helper function that handles retry with small delay, asking the user, and restoring errno. mingw_unlink: include _wchmod in the retry loop (which may fail if the file is locked exclusively). mingw_rmdir: include special error handling in the retry loop. Signed-off-by: Karsten Blees <blees@dcon.de>
This commit is contained in:
parent
a4951f7c7e
commit
3bbd6ca071
102
compat/mingw.c
102
compat/mingw.c
|
@ -24,8 +24,6 @@
|
||||||
|
|
||||||
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
||||||
|
|
||||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
|
||||||
|
|
||||||
void open_in_gdb(void)
|
void open_in_gdb(void)
|
||||||
{
|
{
|
||||||
static struct child_process cp = CHILD_PROCESS_INIT;
|
static struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
|
@ -202,15 +200,12 @@ static int read_yes_no_answer(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ask_yes_no_if_possible(const char *format, ...)
|
static int ask_yes_no_if_possible(const char *format, va_list args)
|
||||||
{
|
{
|
||||||
char question[4096];
|
char question[4096];
|
||||||
const char *retry_hook;
|
const char *retry_hook;
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(question, sizeof(question), format, args);
|
vsnprintf(question, sizeof(question), format, args);
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
retry_hook = mingw_getenv("GIT_ASK_YESNO");
|
retry_hook = mingw_getenv("GIT_ASK_YESNO");
|
||||||
if (retry_hook) {
|
if (retry_hook) {
|
||||||
|
@ -235,6 +230,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int retry_ask_yes_no(int *tries, const char *format, ...)
|
||||||
|
{
|
||||||
|
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||||
|
va_list args;
|
||||||
|
int result, saved_errno = errno;
|
||||||
|
|
||||||
|
if ((*tries) < ARRAY_SIZE(delay)) {
|
||||||
|
/*
|
||||||
|
* We assume that some other process had the file open at the wrong
|
||||||
|
* moment and retry. In order to give the other process a higher
|
||||||
|
* chance to complete its operation, we give up our time slice now.
|
||||||
|
* If we have to retry again, we do sleep a bit.
|
||||||
|
*/
|
||||||
|
Sleep(delay[*tries]);
|
||||||
|
(*tries)++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
result = ask_yes_no_if_possible(format, args);
|
||||||
|
va_end(args);
|
||||||
|
errno = saved_errno;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Windows only */
|
/* Windows only */
|
||||||
enum hide_dotfiles_type {
|
enum hide_dotfiles_type {
|
||||||
HIDE_DOTFILES_FALSE = 0,
|
HIDE_DOTFILES_FALSE = 0,
|
||||||
|
@ -336,7 +356,7 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
||||||
|
|
||||||
int mingw_unlink(const char *pathname)
|
int mingw_unlink(const char *pathname)
|
||||||
{
|
{
|
||||||
int ret, tries = 0;
|
int tries = 0;
|
||||||
wchar_t wpathname[MAX_LONG_PATH];
|
wchar_t wpathname[MAX_LONG_PATH];
|
||||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -344,26 +364,16 @@ int mingw_unlink(const char *pathname)
|
||||||
if (DeleteFileW(wpathname))
|
if (DeleteFileW(wpathname))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* read-only files cannot be removed */
|
do {
|
||||||
_wchmod(wpathname, 0666);
|
/* read-only files cannot be removed */
|
||||||
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
_wchmod(wpathname, 0666);
|
||||||
|
if (!_wunlink(wpathname))
|
||||||
|
return 0;
|
||||||
if (!is_file_in_use_error(GetLastError()))
|
if (!is_file_in_use_error(GetLastError()))
|
||||||
break;
|
break;
|
||||||
/*
|
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
|
||||||
* We assume that some other process had the source or
|
"Should I try again?", pathname));
|
||||||
* destination file open at the wrong moment and retry.
|
return -1;
|
||||||
* In order to give the other process a higher chance to
|
|
||||||
* complete its operation, we give up our time slice now.
|
|
||||||
* If we have to retry again, we do sleep a bit.
|
|
||||||
*/
|
|
||||||
Sleep(delay[tries]);
|
|
||||||
tries++;
|
|
||||||
}
|
|
||||||
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
|
||||||
ask_yes_no_if_possible("Unlink of file '%s' failed. "
|
|
||||||
"Should I try again?", pathname))
|
|
||||||
ret = _wunlink(wpathname);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_dir_empty(const wchar_t *wpath)
|
static int is_dir_empty(const wchar_t *wpath)
|
||||||
|
@ -390,7 +400,7 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||||
|
|
||||||
int mingw_rmdir(const char *pathname)
|
int mingw_rmdir(const char *pathname)
|
||||||
{
|
{
|
||||||
int ret, tries = 0;
|
int tries = 0;
|
||||||
wchar_t wpathname[MAX_LONG_PATH];
|
wchar_t wpathname[MAX_LONG_PATH];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
|
@ -416,7 +426,11 @@ int mingw_rmdir(const char *pathname)
|
||||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
do {
|
||||||
|
if (!_wrmdir(wpathname)) {
|
||||||
|
invalidate_lstat_cache();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (!is_file_in_use_error(GetLastError()))
|
if (!is_file_in_use_error(GetLastError()))
|
||||||
errno = err_win_to_posix(GetLastError());
|
errno = err_win_to_posix(GetLastError());
|
||||||
if (errno != EACCES)
|
if (errno != EACCES)
|
||||||
|
@ -425,23 +439,9 @@ int mingw_rmdir(const char *pathname)
|
||||||
errno = ENOTEMPTY;
|
errno = ENOTEMPTY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/*
|
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
|
||||||
* We assume that some other process had the source or
|
"Should I try again?", pathname));
|
||||||
* destination file open at the wrong moment and retry.
|
return -1;
|
||||||
* In order to give the other process a higher chance to
|
|
||||||
* complete its operation, we give up our time slice now.
|
|
||||||
* If we have to retry again, we do sleep a bit.
|
|
||||||
*/
|
|
||||||
Sleep(delay[tries]);
|
|
||||||
tries++;
|
|
||||||
}
|
|
||||||
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
|
|
||||||
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
|
|
||||||
"Should I try again?", pathname))
|
|
||||||
ret = _wrmdir(wpathname);
|
|
||||||
if (!ret)
|
|
||||||
invalidate_lstat_cache();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int needs_hiding(const char *path)
|
static inline int needs_hiding(const char *path)
|
||||||
|
@ -2445,20 +2445,8 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||||
SetFileAttributesW(wpnew, attrs);
|
SetFileAttributesW(wpnew, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
|
|
||||||
/*
|
|
||||||
* We assume that some other process had the source or
|
|
||||||
* destination file open at the wrong moment and retry.
|
|
||||||
* In order to give the other process a higher chance to
|
|
||||||
* complete its operation, we give up our time slice now.
|
|
||||||
* If we have to retry again, we do sleep a bit.
|
|
||||||
*/
|
|
||||||
Sleep(delay[tries]);
|
|
||||||
tries++;
|
|
||||||
goto repeat;
|
|
||||||
}
|
|
||||||
if (gle == ERROR_ACCESS_DENIED &&
|
if (gle == ERROR_ACCESS_DENIED &&
|
||||||
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
|
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
|
||||||
"Should I try again?", pold, pnew))
|
"Should I try again?", pold, pnew))
|
||||||
goto repeat;
|
goto repeat;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue