Skip to content

Commit c962c9b

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 d854f51 commit c962c9b

File tree

1 file changed

+43
-55
lines changed

1 file changed

+43
-55
lines changed

compat/mingw.c

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

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

14-
static const int delay[] = { 0, 1, 10, 20, 40 };
15-
1614
void open_in_gdb(void)
1715
{
1816
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -188,15 +186,12 @@ static int read_yes_no_answer(void)
188186
return -1;
189187
}
190188

191-
static int ask_yes_no_if_possible(const char *format, ...)
189+
static int ask_yes_no_if_possible(const char *format, va_list args)
192190
{
193191
char question[4096];
194192
const char *retry_hook[] = { NULL, NULL, NULL };
195-
va_list args;
196193

197-
va_start(args, format);
198194
vsnprintf(question, sizeof(question), format, args);
199-
va_end(args);
200195

201196
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
202197
retry_hook[1] = question;
@@ -218,6 +213,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
218213
}
219214
}
220215

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

287307
int mingw_unlink(const char *pathname)
288308
{
289-
int ret, tries = 0;
309+
int tries = 0;
290310
wchar_t wpathname[MAX_LONG_PATH];
291311
if (xutftowcs_long_path(wpathname, pathname) < 0)
292312
return -1;
293313

294-
/* read-only files cannot be removed */
295-
_wchmod(wpathname, 0666);
296-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
314+
do {
315+
/* read-only files cannot be removed */
316+
_wchmod(wpathname, 0666);
317+
if (!_wunlink(wpathname))
318+
return 0;
297319
if (!is_file_in_use_error(GetLastError()))
298320
break;
299-
/*
300-
* We assume that some other process had the source or
301-
* destination file open at the wrong moment and retry.
302-
* In order to give the other process a higher chance to
303-
* complete its operation, we give up our time slice now.
304-
* If we have to retry again, we do sleep a bit.
305-
*/
306-
Sleep(delay[tries]);
307-
tries++;
308-
}
309-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
310-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
311-
"Should I try again?", pathname))
312-
ret = _wunlink(wpathname);
313-
return ret;
321+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
322+
"Should I try again?", pathname));
323+
return -1;
314324
}
315325

316326
static int is_dir_empty(const wchar_t *wpath)
@@ -337,12 +347,14 @@ static int is_dir_empty(const wchar_t *wpath)
337347

338348
int mingw_rmdir(const char *pathname)
339349
{
340-
int ret, tries = 0;
350+
int tries = 0;
341351
wchar_t wpathname[MAX_LONG_PATH];
342352
if (xutftowcs_long_path(wpathname, pathname) < 0)
343353
return -1;
344354

345-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
355+
do {
356+
if (!_wrmdir(wpathname))
357+
return 0;
346358
if (!is_file_in_use_error(GetLastError()))
347359
errno = err_win_to_posix(GetLastError());
348360
if (errno != EACCES)
@@ -351,21 +363,9 @@ int mingw_rmdir(const char *pathname)
351363
errno = ENOTEMPTY;
352364
break;
353365
}
354-
/*
355-
* We assume that some other process had the source or
356-
* destination file open at the wrong moment and retry.
357-
* In order to give the other process a higher chance to
358-
* complete its operation, we give up our time slice now.
359-
* If we have to retry again, we do sleep a bit.
360-
*/
361-
Sleep(delay[tries]);
362-
tries++;
363-
}
364-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
365-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
366-
"Should I try again?", pathname))
367-
ret = _wrmdir(wpathname);
368-
return ret;
366+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
367+
"Should I try again?", pathname));
368+
return -1;
369369
}
370370

371371
static inline int needs_hiding(const char *path)
@@ -1913,20 +1913,8 @@ int mingw_rename(const char *pold, const char *pnew)
19131913
SetFileAttributesW(wpnew, attrs);
19141914
}
19151915
}
1916-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
1917-
/*
1918-
* We assume that some other process had the source or
1919-
* destination file open at the wrong moment and retry.
1920-
* In order to give the other process a higher chance to
1921-
* complete its operation, we give up our time slice now.
1922-
* If we have to retry again, we do sleep a bit.
1923-
*/
1924-
Sleep(delay[tries]);
1925-
tries++;
1926-
goto repeat;
1927-
}
19281916
if (gle == ERROR_ACCESS_DENIED &&
1929-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
1917+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
19301918
"Should I try again?", pold, pnew))
19311919
goto repeat;
19321920

0 commit comments

Comments
 (0)