Skip to content

Commit c19b77e

Browse files
kbleesvdye
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 6a6f646 commit c19b77e

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
@@ -14,8 +14,6 @@
1414

1515
#define HCAST(type, handle) ((type)(intptr_t)handle)
1616

17-
static const int delay[] = { 0, 1, 10, 20, 40 };
18-
1917
void open_in_gdb(void)
2018
{
2119
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -192,15 +190,12 @@ static int read_yes_no_answer(void)
192190
return -1;
193191
}
194192

195-
static int ask_yes_no_if_possible(const char *format, ...)
193+
static int ask_yes_no_if_possible(const char *format, va_list args)
196194
{
197195
char question[4096];
198196
const char *retry_hook[] = { NULL, NULL, NULL };
199-
va_list args;
200197

201-
va_start(args, format);
202198
vsnprintf(question, sizeof(question), format, args);
203-
va_end(args);
204199

205200
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
206201
retry_hook[1] = question;
@@ -222,6 +217,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
222217
}
223218
}
224219

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

301321
int mingw_unlink(const char *pathname)
302322
{
303-
int ret, tries = 0;
323+
int tries = 0;
304324
wchar_t wpathname[MAX_LONG_PATH];
305325
if (xutftowcs_long_path(wpathname, pathname) < 0)
306326
return -1;
307327

308328
if (DeleteFileW(wpathname))
309329
return 0;
310330

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

333343
static int is_dir_empty(const wchar_t *wpath)
@@ -354,7 +364,7 @@ static int is_dir_empty(const wchar_t *wpath)
354364

355365
int mingw_rmdir(const char *pathname)
356366
{
357-
int ret, tries = 0;
367+
int tries = 0;
358368
wchar_t wpathname[MAX_LONG_PATH];
359369
struct stat st;
360370

@@ -380,7 +390,11 @@ int mingw_rmdir(const char *pathname)
380390
if (xutftowcs_long_path(wpathname, pathname) < 0)
381391
return -1;
382392

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

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

0 commit comments

Comments
 (0)