Skip to content

Commit ad1fbe8

Browse files
kbleesdscho
authored andcommitted
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 <[email protected]>
1 parent 2613a02 commit ad1fbe8

File tree

1 file changed

+45
-57
lines changed

1 file changed

+45
-57
lines changed

compat/mingw.c

Lines changed: 45 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
#define HCAST(type, handle) ((type)(intptr_t)handle)
2626

27-
static const int delay[] = { 0, 1, 10, 20, 40 };
28-
2927
void open_in_gdb(void)
3028
{
3129
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -202,15 +200,12 @@ static int read_yes_no_answer(void)
202200
return -1;
203201
}
204202

205-
static int ask_yes_no_if_possible(const char *format, ...)
203+
static int ask_yes_no_if_possible(const char *format, va_list args)
206204
{
207205
char question[4096];
208206
const char *retry_hook;
209-
va_list args;
210207

211-
va_start(args, format);
212208
vsnprintf(question, sizeof(question), format, args);
213-
va_end(args);
214209

215210
retry_hook = mingw_getenv("GIT_ASK_YESNO");
216211
if (retry_hook) {
@@ -235,6 +230,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
235230
}
236231
}
237232

233+
static int retry_ask_yes_no(int *tries, const char *format, ...)
234+
{
235+
static const int delay[] = { 0, 1, 10, 20, 40 };
236+
va_list args;
237+
int result, saved_errno = errno;
238+
239+
if ((*tries) < ARRAY_SIZE(delay)) {
240+
/*
241+
* We assume that some other process had the file open at the wrong
242+
* moment and retry. In order to give the other process a higher
243+
* chance to complete its operation, we give up our time slice now.
244+
* If we have to retry again, we do sleep a bit.
245+
*/
246+
Sleep(delay[*tries]);
247+
(*tries)++;
248+
return 1;
249+
}
250+
251+
va_start(args, format);
252+
result = ask_yes_no_if_possible(format, args);
253+
va_end(args);
254+
errno = saved_errno;
255+
return result;
256+
}
257+
238258
/* Windows only */
239259
enum hide_dotfiles_type {
240260
HIDE_DOTFILES_FALSE = 0,
@@ -336,34 +356,24 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
336356

337357
int mingw_unlink(const char *pathname)
338358
{
339-
int ret, tries = 0;
359+
int tries = 0;
340360
wchar_t wpathname[MAX_LONG_PATH];
341361
if (xutftowcs_long_path(wpathname, pathname) < 0)
342362
return -1;
343363

344364
if (DeleteFileW(wpathname))
345365
return 0;
346366

347-
/* read-only files cannot be removed */
348-
_wchmod(wpathname, 0666);
349-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
367+
do {
368+
/* read-only files cannot be removed */
369+
_wchmod(wpathname, 0666);
370+
if (!_wunlink(wpathname))
371+
return 0;
350372
if (!is_file_in_use_error(GetLastError()))
351373
break;
352-
/*
353-
* We assume that some other process had the source or
354-
* destination file open at the wrong moment and retry.
355-
* In order to give the other process a higher chance to
356-
* complete its operation, we give up our time slice now.
357-
* If we have to retry again, we do sleep a bit.
358-
*/
359-
Sleep(delay[tries]);
360-
tries++;
361-
}
362-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
363-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
364-
"Should I try again?", pathname))
365-
ret = _wunlink(wpathname);
366-
return ret;
374+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
375+
"Should I try again?", pathname));
376+
return -1;
367377
}
368378

369379
static int is_dir_empty(const wchar_t *wpath)
@@ -390,7 +400,7 @@ static int is_dir_empty(const wchar_t *wpath)
390400

391401
int mingw_rmdir(const char *pathname)
392402
{
393-
int ret, tries = 0;
403+
int tries = 0;
394404
wchar_t wpathname[MAX_LONG_PATH];
395405
struct stat st;
396406

@@ -416,7 +426,11 @@ int mingw_rmdir(const char *pathname)
416426
if (xutftowcs_long_path(wpathname, pathname) < 0)
417427
return -1;
418428

419-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
429+
do {
430+
if (!_wrmdir(wpathname)) {
431+
invalidate_lstat_cache();
432+
return 0;
433+
}
420434
if (!is_file_in_use_error(GetLastError()))
421435
errno = err_win_to_posix(GetLastError());
422436
if (errno != EACCES)
@@ -425,23 +439,9 @@ int mingw_rmdir(const char *pathname)
425439
errno = ENOTEMPTY;
426440
break;
427441
}
428-
/*
429-
* We assume that some other process had the source or
430-
* destination file open at the wrong moment and retry.
431-
* In order to give the other process a higher chance to
432-
* complete its operation, we give up our time slice now.
433-
* If we have to retry again, we do sleep a bit.
434-
*/
435-
Sleep(delay[tries]);
436-
tries++;
437-
}
438-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
439-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
440-
"Should I try again?", pathname))
441-
ret = _wrmdir(wpathname);
442-
if (!ret)
443-
invalidate_lstat_cache();
444-
return ret;
442+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
443+
"Should I try again?", pathname));
444+
return -1;
445445
}
446446

447447
static inline int needs_hiding(const char *path)
@@ -2445,20 +2445,8 @@ int mingw_rename(const char *pold, const char *pnew)
24452445
SetFileAttributesW(wpnew, attrs);
24462446
}
24472447
}
2448-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2449-
/*
2450-
* We assume that some other process had the source or
2451-
* destination file open at the wrong moment and retry.
2452-
* In order to give the other process a higher chance to
2453-
* complete its operation, we give up our time slice now.
2454-
* If we have to retry again, we do sleep a bit.
2455-
*/
2456-
Sleep(delay[tries]);
2457-
tries++;
2458-
goto repeat;
2459-
}
24602448
if (gle == ERROR_ACCESS_DENIED &&
2461-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2449+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
24622450
"Should I try again?", pold, pnew))
24632451
goto repeat;
24642452

0 commit comments

Comments
 (0)