Skip to content

Commit 2e061b5

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 602ddfa commit 2e061b5

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
@@ -12,8 +12,6 @@
1212

1313
#define HCAST(type, handle) ((type)(intptr_t)handle)
1414

15-
static const int delay[] = { 0, 1, 10, 20, 40 };
16-
1715
void open_in_gdb(void)
1816
{
1917
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -190,15 +188,12 @@ static int read_yes_no_answer(void)
190188
return -1;
191189
}
192190

193-
static int ask_yes_no_if_possible(const char *format, ...)
191+
static int ask_yes_no_if_possible(const char *format, va_list args)
194192
{
195193
char question[4096];
196194
const char *retry_hook[] = { NULL, NULL, NULL };
197-
va_list args;
198195

199-
va_start(args, format);
200196
vsnprintf(question, sizeof(question), format, args);
201-
va_end(args);
202197

203198
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
204199
retry_hook[1] = question;
@@ -220,6 +215,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
220215
}
221216
}
222217

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

299319
int mingw_unlink(const char *pathname)
300320
{
301-
int ret, tries = 0;
321+
int tries = 0;
302322
wchar_t wpathname[MAX_LONG_PATH];
303323
if (xutftowcs_long_path(wpathname, pathname) < 0)
304324
return -1;
305325

306326
if (DeleteFileW(wpathname))
307327
return 0;
308328

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

331341
static int is_dir_empty(const wchar_t *wpath)
@@ -352,7 +362,7 @@ static int is_dir_empty(const wchar_t *wpath)
352362

353363
int mingw_rmdir(const char *pathname)
354364
{
355-
int ret, tries = 0;
365+
int tries = 0;
356366
wchar_t wpathname[MAX_LONG_PATH];
357367
struct stat st;
358368

@@ -378,7 +388,11 @@ int mingw_rmdir(const char *pathname)
378388
if (xutftowcs_long_path(wpathname, pathname) < 0)
379389
return -1;
380390

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

409409
static inline int needs_hiding(const char *path)
@@ -2335,20 +2335,8 @@ int mingw_rename(const char *pold, const char *pnew)
23352335
SetFileAttributesW(wpnew, attrs);
23362336
}
23372337
}
2338-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2339-
/*
2340-
* We assume that some other process had the source or
2341-
* destination file open at the wrong moment and retry.
2342-
* In order to give the other process a higher chance to
2343-
* complete its operation, we give up our time slice now.
2344-
* If we have to retry again, we do sleep a bit.
2345-
*/
2346-
Sleep(delay[tries]);
2347-
tries++;
2348-
goto repeat;
2349-
}
23502338
if (gle == ERROR_ACCESS_DENIED &&
2351-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2339+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
23522340
"Should I try again?", pold, pnew))
23532341
goto repeat;
23542342

0 commit comments

Comments
 (0)