Skip to content

Commit 9ab0f68

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 <blees@dcon.de>
1 parent 9b17d64 commit 9ab0f68

File tree

1 file changed

+43
-55
lines changed

1 file changed

+43
-55
lines changed

compat/mingw.c

+43-55
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
#define HCAST(type, handle) ((type)(intptr_t)handle)
1111

12-
static const int delay[] = { 0, 1, 10, 20, 40 };
13-
1412
int err_win_to_posix(DWORD winerr)
1513
{
1614
int error = ENOSYS;
@@ -173,15 +171,12 @@ static int read_yes_no_answer(void)
173171
return -1;
174172
}
175173

176-
static int ask_yes_no_if_possible(const char *format, ...)
174+
static int ask_yes_no_if_possible(const char *format, va_list args)
177175
{
178176
char question[4096];
179177
const char *retry_hook[] = { NULL, NULL, NULL };
180-
va_list args;
181178

182-
va_start(args, format);
183179
vsnprintf(question, sizeof(question), format, args);
184-
va_end(args);
185180

186181
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
187182
retry_hook[1] = question;
@@ -203,6 +198,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
203198
}
204199
}
205200

201+
static int retry_ask_yes_no(int *tries, const char *format, ...)
202+
{
203+
static const int delay[] = { 0, 1, 10, 20, 40 };
204+
va_list args;
205+
int result, saved_errno = errno;
206+
207+
if ((*tries) < ARRAY_SIZE(delay)) {
208+
/*
209+
* We assume that some other process had the file open at the wrong
210+
* moment and retry. In order to give the other process a higher
211+
* chance to complete its operation, we give up our time slice now.
212+
* If we have to retry again, we do sleep a bit.
213+
*/
214+
Sleep(delay[*tries]);
215+
(*tries)++;
216+
return 1;
217+
}
218+
219+
va_start(args, format);
220+
result = ask_yes_no_if_possible(format, args);
221+
va_end(args);
222+
errno = saved_errno;
223+
return result;
224+
}
225+
206226
/* Windows only */
207227
enum hide_dotfiles_type {
208228
HIDE_DOTFILES_FALSE = 0,
@@ -271,31 +291,21 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
271291

272292
int mingw_unlink(const char *pathname)
273293
{
274-
int ret, tries = 0;
294+
int tries = 0;
275295
wchar_t wpathname[MAX_LONG_PATH];
276296
if (xutftowcs_long_path(wpathname, pathname) < 0)
277297
return -1;
278298

279-
/* read-only files cannot be removed */
280-
_wchmod(wpathname, 0666);
281-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
299+
do {
300+
/* read-only files cannot be removed */
301+
_wchmod(wpathname, 0666);
302+
if (!_wunlink(wpathname))
303+
return 0;
282304
if (!is_file_in_use_error(GetLastError()))
283305
break;
284-
/*
285-
* We assume that some other process had the source or
286-
* destination file open at the wrong moment and retry.
287-
* In order to give the other process a higher chance to
288-
* complete its operation, we give up our time slice now.
289-
* If we have to retry again, we do sleep a bit.
290-
*/
291-
Sleep(delay[tries]);
292-
tries++;
293-
}
294-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
295-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
296-
"Should I try again?", pathname))
297-
ret = _wunlink(wpathname);
298-
return ret;
306+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
307+
"Should I try again?", pathname));
308+
return -1;
299309
}
300310

301311
static int is_dir_empty(const wchar_t *wpath)
@@ -322,12 +332,14 @@ static int is_dir_empty(const wchar_t *wpath)
322332

323333
int mingw_rmdir(const char *pathname)
324334
{
325-
int ret, tries = 0;
335+
int tries = 0;
326336
wchar_t wpathname[MAX_LONG_PATH];
327337
if (xutftowcs_long_path(wpathname, pathname) < 0)
328338
return -1;
329339

330-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
340+
do {
341+
if (!_wrmdir(wpathname))
342+
return 0;
331343
if (!is_file_in_use_error(GetLastError()))
332344
errno = err_win_to_posix(GetLastError());
333345
if (errno != EACCES)
@@ -336,21 +348,9 @@ int mingw_rmdir(const char *pathname)
336348
errno = ENOTEMPTY;
337349
break;
338350
}
339-
/*
340-
* We assume that some other process had the source or
341-
* destination file open at the wrong moment and retry.
342-
* In order to give the other process a higher chance to
343-
* complete its operation, we give up our time slice now.
344-
* If we have to retry again, we do sleep a bit.
345-
*/
346-
Sleep(delay[tries]);
347-
tries++;
348-
}
349-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
350-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
351-
"Should I try again?", pathname))
352-
ret = _wrmdir(wpathname);
353-
return ret;
351+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
352+
"Should I try again?", pathname));
353+
return -1;
354354
}
355355

356356
static inline int needs_hiding(const char *path)
@@ -2010,20 +2010,8 @@ int mingw_rename(const char *pold, const char *pnew)
20102010
SetFileAttributesW(wpnew, attrs);
20112011
}
20122012
}
2013-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2014-
/*
2015-
* We assume that some other process had the source or
2016-
* destination file open at the wrong moment and retry.
2017-
* In order to give the other process a higher chance to
2018-
* complete its operation, we give up our time slice now.
2019-
* If we have to retry again, we do sleep a bit.
2020-
*/
2021-
Sleep(delay[tries]);
2022-
tries++;
2023-
goto repeat;
2024-
}
20252013
if (gle == ERROR_ACCESS_DENIED &&
2026-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2014+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
20272015
"Should I try again?", pold, pnew))
20282016
goto repeat;
20292017

0 commit comments

Comments
 (0)