Skip to content

Commit cf2f5ee

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 c98cbce commit cf2f5ee

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
@@ -10,8 +10,6 @@
1010

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

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

177-
static int ask_yes_no_if_possible(const char *format, ...)
175+
static int ask_yes_no_if_possible(const char *format, va_list args)
178176
{
179177
char question[4096];
180178
const char *retry_hook[] = { NULL, NULL, NULL };
181-
va_list args;
182179

183-
va_start(args, format);
184180
vsnprintf(question, sizeof(question), format, args);
185-
va_end(args);
186181

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

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

273293
int mingw_unlink(const char *pathname)
274294
{
275-
int ret, tries = 0;
295+
int tries = 0;
276296
wchar_t wpathname[MAX_LONG_PATH];
277297
if (xutftowcs_long_path(wpathname, pathname) < 0)
278298
return -1;
279299

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

302312
static int is_dir_empty(const wchar_t *wpath)
@@ -323,12 +333,14 @@ static int is_dir_empty(const wchar_t *wpath)
323333

324334
int mingw_rmdir(const char *pathname)
325335
{
326-
int ret, tries = 0;
336+
int tries = 0;
327337
wchar_t wpathname[MAX_LONG_PATH];
328338
if (xutftowcs_long_path(wpathname, pathname) < 0)
329339
return -1;
330340

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

357357
static inline int needs_hiding(const char *path)
@@ -2049,20 +2049,8 @@ int mingw_rename(const char *pold, const char *pnew)
20492049
SetFileAttributesW(wpnew, attrs);
20502050
}
20512051
}
2052-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2053-
/*
2054-
* We assume that some other process had the source or
2055-
* destination file open at the wrong moment and retry.
2056-
* In order to give the other process a higher chance to
2057-
* complete its operation, we give up our time slice now.
2058-
* If we have to retry again, we do sleep a bit.
2059-
*/
2060-
Sleep(delay[tries]);
2061-
tries++;
2062-
goto repeat;
2063-
}
20642052
if (gle == ERROR_ACCESS_DENIED &&
2065-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2053+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
20662054
"Should I try again?", pold, pnew))
20672055
goto repeat;
20682056

0 commit comments

Comments
 (0)