Skip to content

Commit f01e3ed

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 f72f400 commit f01e3ed

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;
@@ -203,15 +201,12 @@ static int read_yes_no_answer(void)
203201
return -1;
204202
}
205203

206-
static int ask_yes_no_if_possible(const char *format, ...)
204+
static int ask_yes_no_if_possible(const char *format, va_list args)
207205
{
208206
char question[4096];
209207
const char *retry_hook;
210-
va_list args;
211208

212-
va_start(args, format);
213209
vsnprintf(question, sizeof(question), format, args);
214-
va_end(args);
215210

216211
retry_hook = mingw_getenv("GIT_ASK_YESNO");
217212
if (retry_hook) {
@@ -236,6 +231,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
236231
}
237232
}
238233

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

330350
int mingw_unlink(const char *pathname)
331351
{
332-
int ret, tries = 0;
352+
int tries = 0;
333353
wchar_t wpathname[MAX_LONG_PATH];
334354
if (xutftowcs_long_path(wpathname, pathname) < 0)
335355
return -1;
336356

337357
if (DeleteFileW(wpathname))
338358
return 0;
339359

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

362372
static int is_dir_empty(const wchar_t *wpath)
@@ -383,7 +393,7 @@ static int is_dir_empty(const wchar_t *wpath)
383393

384394
int mingw_rmdir(const char *pathname)
385395
{
386-
int ret, tries = 0;
396+
int tries = 0;
387397
wchar_t wpathname[MAX_LONG_PATH];
388398
struct stat st;
389399

@@ -409,7 +419,11 @@ int mingw_rmdir(const char *pathname)
409419
if (xutftowcs_long_path(wpathname, pathname) < 0)
410420
return -1;
411421

412-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
422+
do {
423+
if (!_wrmdir(wpathname)) {
424+
invalidate_lstat_cache();
425+
return 0;
426+
}
413427
if (!is_file_in_use_error(GetLastError()))
414428
errno = err_win_to_posix(GetLastError());
415429
if (errno != EACCES)
@@ -418,23 +432,9 @@ int mingw_rmdir(const char *pathname)
418432
errno = ENOTEMPTY;
419433
break;
420434
}
421-
/*
422-
* We assume that some other process had the source or
423-
* destination file open at the wrong moment and retry.
424-
* In order to give the other process a higher chance to
425-
* complete its operation, we give up our time slice now.
426-
* If we have to retry again, we do sleep a bit.
427-
*/
428-
Sleep(delay[tries]);
429-
tries++;
430-
}
431-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
432-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
433-
"Should I try again?", pathname))
434-
ret = _wrmdir(wpathname);
435-
if (!ret)
436-
invalidate_lstat_cache();
437-
return ret;
435+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
436+
"Should I try again?", pathname));
437+
return -1;
438438
}
439439

440440
static inline int needs_hiding(const char *path)
@@ -2421,20 +2421,8 @@ int mingw_rename(const char *pold, const char *pnew)
24212421
SetFileAttributesW(wpnew, attrs);
24222422
}
24232423
}
2424-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2425-
/*
2426-
* We assume that some other process had the source or
2427-
* destination file open at the wrong moment and retry.
2428-
* In order to give the other process a higher chance to
2429-
* complete its operation, we give up our time slice now.
2430-
* If we have to retry again, we do sleep a bit.
2431-
*/
2432-
Sleep(delay[tries]);
2433-
tries++;
2434-
goto repeat;
2435-
}
24362424
if (gle == ERROR_ACCESS_DENIED &&
2437-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2425+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
24382426
"Should I try again?", pold, pnew))
24392427
goto repeat;
24402428

0 commit comments

Comments
 (0)