Skip to content

Commit ffa98c1

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 b6a259c commit ffa98c1

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
@@ -25,8 +25,6 @@
2525

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

28-
static const int delay[] = { 0, 1, 10, 20, 40 };
29-
3028
void open_in_gdb(void)
3129
{
3230
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,
@@ -337,34 +357,24 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
337357

338358
int mingw_unlink(const char *pathname)
339359
{
340-
int ret, tries = 0;
360+
int tries = 0;
341361
wchar_t wpathname[MAX_LONG_PATH];
342362
if (xutftowcs_long_path(wpathname, pathname) < 0)
343363
return -1;
344364

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

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

370380
static int is_dir_empty(const wchar_t *wpath)
@@ -391,7 +401,7 @@ static int is_dir_empty(const wchar_t *wpath)
391401

392402
int mingw_rmdir(const char *pathname)
393403
{
394-
int ret, tries = 0;
404+
int tries = 0;
395405
wchar_t wpathname[MAX_LONG_PATH];
396406
struct stat st;
397407

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

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

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

0 commit comments

Comments
 (0)