Skip to content

Commit 7af4c9f

Browse files
kbleesdscho
authored andcommittedFeb 6, 2025
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>
1 parent f322681 commit 7af4c9f

File tree

1 file changed

+45
-57
lines changed

1 file changed

+45
-57
lines changed
 

‎compat/mingw.c

+45-57
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727

2828
#define HCAST(type, handle) ((type)(intptr_t)handle)
2929

30-
static const int delay[] = { 0, 1, 10, 20, 40 };
31-
3230
void open_in_gdb(void)
3331
{
3432
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -204,15 +202,12 @@ static int read_yes_no_answer(void)
204202
return -1;
205203
}
206204

207-
static int ask_yes_no_if_possible(const char *format, ...)
205+
static int ask_yes_no_if_possible(const char *format, va_list args)
208206
{
209207
char question[4096];
210208
const char *retry_hook;
211-
va_list args;
212209

213-
va_start(args, format);
214210
vsnprintf(question, sizeof(question), format, args);
215-
va_end(args);
216211

217212
retry_hook = mingw_getenv("GIT_ASK_YESNO");
218213
if (retry_hook) {
@@ -237,6 +232,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
237232
}
238233
}
239234

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

340360
int mingw_unlink(const char *pathname)
341361
{
342-
int ret, tries = 0;
362+
int tries = 0;
343363
wchar_t wpathname[MAX_LONG_PATH];
344364
if (xutftowcs_long_path(wpathname, pathname) < 0)
345365
return -1;
346366

347367
if (DeleteFileW(wpathname))
348368
return 0;
349369

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

372382
static int is_dir_empty(const wchar_t *wpath)
@@ -393,7 +403,7 @@ static int is_dir_empty(const wchar_t *wpath)
393403

394404
int mingw_rmdir(const char *pathname)
395405
{
396-
int ret, tries = 0;
406+
int tries = 0;
397407
wchar_t wpathname[MAX_LONG_PATH];
398408
struct stat st;
399409

@@ -419,7 +429,11 @@ int mingw_rmdir(const char *pathname)
419429
if (xutftowcs_long_path(wpathname, pathname) < 0)
420430
return -1;
421431

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

450450
static inline int needs_hiding(const char *path)
@@ -2683,20 +2683,8 @@ int mingw_rename(const char *pold, const char *pnew)
26832683
SetFileAttributesW(wpnew, attrs);
26842684
}
26852685
}
2686-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2687-
/*
2688-
* We assume that some other process had the source or
2689-
* destination file open at the wrong moment and retry.
2690-
* In order to give the other process a higher chance to
2691-
* complete its operation, we give up our time slice now.
2692-
* If we have to retry again, we do sleep a bit.
2693-
*/
2694-
Sleep(delay[tries]);
2695-
tries++;
2696-
goto repeat;
2697-
}
26982686
if (gle == ERROR_ACCESS_DENIED &&
2699-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2687+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
27002688
"Should I try again?", pold, pnew))
27012689
goto repeat;
27022690

0 commit comments

Comments
 (0)