Skip to content

Commit 178ced4

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 657a344 commit 178ced4

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
@@ -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[] = { NULL, NULL, NULL };
201-
va_list args;
202199

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

207202
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
208203
retry_hook[1] = question;
@@ -224,6 +219,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
224219
}
225220
}
226221

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

303323
int mingw_unlink(const char *pathname)
304324
{
305-
int ret, tries = 0;
325+
int tries = 0;
306326
wchar_t wpathname[MAX_LONG_PATH];
307327
if (xutftowcs_long_path(wpathname, pathname) < 0)
308328
return -1;
309329

310330
if (DeleteFileW(wpathname))
311331
return 0;
312332

313-
/* read-only files cannot be removed */
314-
_wchmod(wpathname, 0666);
315-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
333+
do {
334+
/* read-only files cannot be removed */
335+
_wchmod(wpathname, 0666);
336+
if (!_wunlink(wpathname))
337+
return 0;
316338
if (!is_file_in_use_error(GetLastError()))
317339
break;
318-
/*
319-
* We assume that some other process had the source or
320-
* destination file open at the wrong moment and retry.
321-
* In order to give the other process a higher chance to
322-
* complete its operation, we give up our time slice now.
323-
* If we have to retry again, we do sleep a bit.
324-
*/
325-
Sleep(delay[tries]);
326-
tries++;
327-
}
328-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
329-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
330-
"Should I try again?", pathname))
331-
ret = _wunlink(wpathname);
332-
return ret;
340+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
341+
"Should I try again?", pathname));
342+
return -1;
333343
}
334344

335345
static int is_dir_empty(const wchar_t *wpath)
@@ -356,7 +366,7 @@ static int is_dir_empty(const wchar_t *wpath)
356366

357367
int mingw_rmdir(const char *pathname)
358368
{
359-
int ret, tries = 0;
369+
int tries = 0;
360370
wchar_t wpathname[MAX_LONG_PATH];
361371
struct stat st;
362372

@@ -382,7 +392,11 @@ int mingw_rmdir(const char *pathname)
382392
if (xutftowcs_long_path(wpathname, pathname) < 0)
383393
return -1;
384394

385-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
395+
do {
396+
if (!_wrmdir(wpathname)) {
397+
invalidate_lstat_cache();
398+
return 0;
399+
}
386400
if (!is_file_in_use_error(GetLastError()))
387401
errno = err_win_to_posix(GetLastError());
388402
if (errno != EACCES)
@@ -391,23 +405,9 @@ int mingw_rmdir(const char *pathname)
391405
errno = ENOTEMPTY;
392406
break;
393407
}
394-
/*
395-
* We assume that some other process had the source or
396-
* destination file open at the wrong moment and retry.
397-
* In order to give the other process a higher chance to
398-
* complete its operation, we give up our time slice now.
399-
* If we have to retry again, we do sleep a bit.
400-
*/
401-
Sleep(delay[tries]);
402-
tries++;
403-
}
404-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
405-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
406-
"Should I try again?", pathname))
407-
ret = _wrmdir(wpathname);
408-
if (!ret)
409-
invalidate_lstat_cache();
410-
return ret;
408+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
409+
"Should I try again?", pathname));
410+
return -1;
411411
}
412412

413413
static inline int needs_hiding(const char *path)
@@ -2360,20 +2360,8 @@ int mingw_rename(const char *pold, const char *pnew)
23602360
SetFileAttributesW(wpnew, attrs);
23612361
}
23622362
}
2363-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2364-
/*
2365-
* We assume that some other process had the source or
2366-
* destination file open at the wrong moment and retry.
2367-
* In order to give the other process a higher chance to
2368-
* complete its operation, we give up our time slice now.
2369-
* If we have to retry again, we do sleep a bit.
2370-
*/
2371-
Sleep(delay[tries]);
2372-
tries++;
2373-
goto repeat;
2374-
}
23752363
if (gle == ERROR_ACCESS_DENIED &&
2376-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2364+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
23772365
"Should I try again?", pold, pnew))
23782366
goto repeat;
23792367

0 commit comments

Comments
 (0)