Skip to content

Commit 48c5d96

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 d79c5e4 commit 48c5d96

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,
@@ -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)
@@ -2426,20 +2426,8 @@ int mingw_rename(const char *pold, const char *pnew)
24262426
SetFileAttributesW(wpnew, attrs);
24272427
}
24282428
}
2429-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2430-
/*
2431-
* We assume that some other process had the source or
2432-
* destination file open at the wrong moment and retry.
2433-
* In order to give the other process a higher chance to
2434-
* complete its operation, we give up our time slice now.
2435-
* If we have to retry again, we do sleep a bit.
2436-
*/
2437-
Sleep(delay[tries]);
2438-
tries++;
2439-
goto repeat;
2440-
}
24412429
if (gle == ERROR_ACCESS_DENIED &&
2442-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2430+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
24432431
"Should I try again?", pold, pnew))
24442432
goto repeat;
24452433

0 commit comments

Comments
 (0)