Skip to content

Commit 70a290a

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 08ae545 commit 70a290a

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
@@ -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;
@@ -189,15 +187,12 @@ static int read_yes_no_answer(void)
189187
return -1;
190188
}
191189

192-
static int ask_yes_no_if_possible(const char *format, ...)
190+
static int ask_yes_no_if_possible(const char *format, va_list args)
193191
{
194192
char question[4096];
195193
const char *retry_hook[] = { NULL, NULL, NULL };
196-
va_list args;
197194

198-
va_start(args, format);
199195
vsnprintf(question, sizeof(question), format, args);
200-
va_end(args);
201196

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

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

288308
int mingw_unlink(const char *pathname)
289309
{
290-
int ret, tries = 0;
310+
int tries = 0;
291311
wchar_t wpathname[MAX_LONG_PATH];
292312
if (xutftowcs_long_path(wpathname, pathname) < 0)
293313
return -1;
294314

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

317327
static int is_dir_empty(const wchar_t *wpath)
@@ -338,12 +348,14 @@ static int is_dir_empty(const wchar_t *wpath)
338348

339349
int mingw_rmdir(const char *pathname)
340350
{
341-
int ret, tries = 0;
351+
int tries = 0;
342352
wchar_t wpathname[MAX_LONG_PATH];
343353
if (xutftowcs_long_path(wpathname, pathname) < 0)
344354
return -1;
345355

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

372372
static inline int needs_hiding(const char *path)
@@ -1921,20 +1921,8 @@ int mingw_rename(const char *pold, const char *pnew)
19211921
SetFileAttributesW(wpnew, attrs);
19221922
}
19231923
}
1924-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
1925-
/*
1926-
* We assume that some other process had the source or
1927-
* destination file open at the wrong moment and retry.
1928-
* In order to give the other process a higher chance to
1929-
* complete its operation, we give up our time slice now.
1930-
* If we have to retry again, we do sleep a bit.
1931-
*/
1932-
Sleep(delay[tries]);
1933-
tries++;
1934-
goto repeat;
1935-
}
19361924
if (gle == ERROR_ACCESS_DENIED &&
1937-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
1925+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
19381926
"Should I try again?", pold, pnew))
19391927
goto repeat;
19401928

0 commit comments

Comments
 (0)