Skip to content

Commit 31c2295

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 cab2d36 commit 31c2295

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
@@ -16,8 +16,6 @@
1616

1717
#define HCAST(type, handle) ((type)(intptr_t)handle)
1818

19-
static const int delay[] = { 0, 1, 10, 20, 40 };
20-
2119
void open_in_gdb(void)
2220
{
2321
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -194,15 +192,12 @@ static int read_yes_no_answer(void)
194192
return -1;
195193
}
196194

197-
static int ask_yes_no_if_possible(const char *format, ...)
195+
static int ask_yes_no_if_possible(const char *format, va_list args)
198196
{
199197
char question[4096];
200198
const char *retry_hook;
201-
va_list args;
202199

203-
va_start(args, format);
204200
vsnprintf(question, sizeof(question), format, args);
205-
va_end(args);
206201

207202
retry_hook = mingw_getenv("GIT_ASK_YESNO");
208203
if (retry_hook) {
@@ -227,6 +222,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
227222
}
228223
}
229224

225+
static int retry_ask_yes_no(int *tries, const char *format, ...)
226+
{
227+
static const int delay[] = { 0, 1, 10, 20, 40 };
228+
va_list args;
229+
int result, saved_errno = errno;
230+
231+
if ((*tries) < ARRAY_SIZE(delay)) {
232+
/*
233+
* We assume that some other process had the file open at the wrong
234+
* moment and retry. In order to give the other process a higher
235+
* chance to complete its operation, we give up our time slice now.
236+
* If we have to retry again, we do sleep a bit.
237+
*/
238+
Sleep(delay[*tries]);
239+
(*tries)++;
240+
return 1;
241+
}
242+
243+
va_start(args, format);
244+
result = ask_yes_no_if_possible(format, args);
245+
va_end(args);
246+
errno = saved_errno;
247+
return result;
248+
}
249+
230250
/* Windows only */
231251
enum hide_dotfiles_type {
232252
HIDE_DOTFILES_FALSE = 0,
@@ -320,34 +340,24 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
320340

321341
int mingw_unlink(const char *pathname)
322342
{
323-
int ret, tries = 0;
343+
int tries = 0;
324344
wchar_t wpathname[MAX_LONG_PATH];
325345
if (xutftowcs_long_path(wpathname, pathname) < 0)
326346
return -1;
327347

328348
if (DeleteFileW(wpathname))
329349
return 0;
330350

331-
/* read-only files cannot be removed */
332-
_wchmod(wpathname, 0666);
333-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
351+
do {
352+
/* read-only files cannot be removed */
353+
_wchmod(wpathname, 0666);
354+
if (!_wunlink(wpathname))
355+
return 0;
334356
if (!is_file_in_use_error(GetLastError()))
335357
break;
336-
/*
337-
* We assume that some other process had the source or
338-
* destination file open at the wrong moment and retry.
339-
* In order to give the other process a higher chance to
340-
* complete its operation, we give up our time slice now.
341-
* If we have to retry again, we do sleep a bit.
342-
*/
343-
Sleep(delay[tries]);
344-
tries++;
345-
}
346-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
347-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
348-
"Should I try again?", pathname))
349-
ret = _wunlink(wpathname);
350-
return ret;
358+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
359+
"Should I try again?", pathname));
360+
return -1;
351361
}
352362

353363
static int is_dir_empty(const wchar_t *wpath)
@@ -374,7 +384,7 @@ static int is_dir_empty(const wchar_t *wpath)
374384

375385
int mingw_rmdir(const char *pathname)
376386
{
377-
int ret, tries = 0;
387+
int tries = 0;
378388
wchar_t wpathname[MAX_LONG_PATH];
379389
struct stat st;
380390

@@ -400,7 +410,11 @@ int mingw_rmdir(const char *pathname)
400410
if (xutftowcs_long_path(wpathname, pathname) < 0)
401411
return -1;
402412

403-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
413+
do {
414+
if (!_wrmdir(wpathname)) {
415+
invalidate_lstat_cache();
416+
return 0;
417+
}
404418
if (!is_file_in_use_error(GetLastError()))
405419
errno = err_win_to_posix(GetLastError());
406420
if (errno != EACCES)
@@ -409,23 +423,9 @@ int mingw_rmdir(const char *pathname)
409423
errno = ENOTEMPTY;
410424
break;
411425
}
412-
/*
413-
* We assume that some other process had the source or
414-
* destination file open at the wrong moment and retry.
415-
* In order to give the other process a higher chance to
416-
* complete its operation, we give up our time slice now.
417-
* If we have to retry again, we do sleep a bit.
418-
*/
419-
Sleep(delay[tries]);
420-
tries++;
421-
}
422-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
423-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
424-
"Should I try again?", pathname))
425-
ret = _wrmdir(wpathname);
426-
if (!ret)
427-
invalidate_lstat_cache();
428-
return ret;
426+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
427+
"Should I try again?", pathname));
428+
return -1;
429429
}
430430

431431
static inline int needs_hiding(const char *path)
@@ -2403,20 +2403,8 @@ int mingw_rename(const char *pold, const char *pnew)
24032403
SetFileAttributesW(wpnew, attrs);
24042404
}
24052405
}
2406-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2407-
/*
2408-
* We assume that some other process had the source or
2409-
* destination file open at the wrong moment and retry.
2410-
* In order to give the other process a higher chance to
2411-
* complete its operation, we give up our time slice now.
2412-
* If we have to retry again, we do sleep a bit.
2413-
*/
2414-
Sleep(delay[tries]);
2415-
tries++;
2416-
goto repeat;
2417-
}
24182406
if (gle == ERROR_ACCESS_DENIED &&
2419-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2407+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
24202408
"Should I try again?", pold, pnew))
24212409
goto repeat;
24222410

0 commit comments

Comments
 (0)