Skip to content

Commit 3bded6b

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 7401e3b commit 3bded6b

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
@@ -15,8 +15,6 @@
1515

1616
#define HCAST(type, handle) ((type)(intptr_t)handle)
1717

18-
static const int delay[] = { 0, 1, 10, 20, 40 };
19-
2018
void open_in_gdb(void)
2119
{
2220
static struct child_process cp = CHILD_PROCESS_INIT;
@@ -193,15 +191,12 @@ static int read_yes_no_answer(void)
193191
return -1;
194192
}
195193

196-
static int ask_yes_no_if_possible(const char *format, ...)
194+
static int ask_yes_no_if_possible(const char *format, va_list args)
197195
{
198196
char question[4096];
199197
const char *retry_hook[] = { NULL, NULL, NULL };
200-
va_list args;
201198

202-
va_start(args, format);
203199
vsnprintf(question, sizeof(question), format, args);
204-
va_end(args);
205200

206201
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
207202
retry_hook[1] = question;
@@ -223,6 +218,31 @@ static int ask_yes_no_if_possible(const char *format, ...)
223218
}
224219
}
225220

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

302322
int mingw_unlink(const char *pathname)
303323
{
304-
int ret, tries = 0;
324+
int tries = 0;
305325
wchar_t wpathname[MAX_LONG_PATH];
306326
if (xutftowcs_long_path(wpathname, pathname) < 0)
307327
return -1;
308328

309329
if (DeleteFileW(wpathname))
310330
return 0;
311331

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

334344
static int is_dir_empty(const wchar_t *wpath)
@@ -355,7 +365,7 @@ static int is_dir_empty(const wchar_t *wpath)
355365

356366
int mingw_rmdir(const char *pathname)
357367
{
358-
int ret, tries = 0;
368+
int tries = 0;
359369
wchar_t wpathname[MAX_LONG_PATH];
360370
struct stat st;
361371

@@ -381,7 +391,11 @@ int mingw_rmdir(const char *pathname)
381391
if (xutftowcs_long_path(wpathname, pathname) < 0)
382392
return -1;
383393

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

412412
static inline int needs_hiding(const char *path)
@@ -2344,20 +2344,8 @@ int mingw_rename(const char *pold, const char *pnew)
23442344
SetFileAttributesW(wpnew, attrs);
23452345
}
23462346
}
2347-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
2348-
/*
2349-
* We assume that some other process had the source or
2350-
* destination file open at the wrong moment and retry.
2351-
* In order to give the other process a higher chance to
2352-
* complete its operation, we give up our time slice now.
2353-
* If we have to retry again, we do sleep a bit.
2354-
*/
2355-
Sleep(delay[tries]);
2356-
tries++;
2357-
goto repeat;
2358-
}
23592347
if (gle == ERROR_ACCESS_DENIED &&
2360-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
2348+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
23612349
"Should I try again?", pold, pnew))
23622350
goto repeat;
23632351

0 commit comments

Comments
 (0)