Skip to content

Commit 16e9ff4

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 42b78a1 commit 16e9ff4

File tree

1 file changed

+43
-53
lines changed

1 file changed

+43
-53
lines changed

compat/mingw.c

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,12 @@ static int read_yes_no_answer(void)
172172
return -1;
173173
}
174174

175-
static int ask_yes_no_if_possible(const char *format, ...)
175+
static int ask_yes_no_if_possible(const char *format, va_list args)
176176
{
177177
char question[4096];
178178
const char *retry_hook[] = { NULL, NULL, NULL };
179-
va_list args;
180179

181-
va_start(args, format);
182180
vsnprintf(question, sizeof(question), format, args);
183-
va_end(args);
184181

185182
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
186183
retry_hook[1] = question;
@@ -202,33 +199,48 @@ static int ask_yes_no_if_possible(const char *format, ...)
202199
}
203200
}
204201

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+
205227
int mingw_unlink(const char *pathname)
206228
{
207-
int ret, tries = 0;
229+
int tries = 0;
208230
wchar_t wpathname[MAX_LONG_PATH];
209231
if (xutftowcs_long_path(wpathname, pathname) < 0)
210232
return -1;
211233

212-
/* read-only files cannot be removed */
213-
_wchmod(wpathname, 0666);
214-
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
234+
do {
235+
/* read-only files cannot be removed */
236+
_wchmod(wpathname, 0666);
237+
if (!_wunlink(wpathname))
238+
return 0;
215239
if (!is_file_in_use_error(GetLastError()))
216240
break;
217-
/*
218-
* We assume that some other process had the source or
219-
* destination file open at the wrong moment and retry.
220-
* In order to give the other process a higher chance to
221-
* complete its operation, we give up our time slice now.
222-
* If we have to retry again, we do sleep a bit.
223-
*/
224-
Sleep(delay[tries]);
225-
tries++;
226-
}
227-
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
228-
ask_yes_no_if_possible("Unlink of file '%s' failed. "
229-
"Should I try again?", pathname))
230-
ret = _wunlink(wpathname);
231-
return ret;
241+
} while (retry_ask_yes_no(&tries, "Unlink of file '%s' failed. "
242+
"Should I try again?", pathname));
243+
return -1;
232244
}
233245

234246
static int is_dir_empty(const wchar_t *wpath)
@@ -255,12 +267,14 @@ static int is_dir_empty(const wchar_t *wpath)
255267

256268
int mingw_rmdir(const char *pathname)
257269
{
258-
int ret, tries = 0;
270+
int tries = 0;
259271
wchar_t wpathname[MAX_LONG_PATH];
260272
if (xutftowcs_long_path(wpathname, pathname) < 0)
261273
return -1;
262274

263-
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
275+
do {
276+
if (!_wrmdir(wpathname))
277+
return 0;
264278
if (!is_file_in_use_error(GetLastError()))
265279
errno = err_win_to_posix(GetLastError());
266280
if (errno != EACCES)
@@ -269,21 +283,9 @@ int mingw_rmdir(const char *pathname)
269283
errno = ENOTEMPTY;
270284
break;
271285
}
272-
/*
273-
* We assume that some other process had the source or
274-
* destination file open at the wrong moment and retry.
275-
* In order to give the other process a higher chance to
276-
* complete its operation, we give up our time slice now.
277-
* If we have to retry again, we do sleep a bit.
278-
*/
279-
Sleep(delay[tries]);
280-
tries++;
281-
}
282-
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
283-
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
284-
"Should I try again?", pathname))
285-
ret = _wrmdir(wpathname);
286-
return ret;
286+
} while (retry_ask_yes_no(&tries, "Deletion of directory '%s' failed. "
287+
"Should I try again?", pathname));
288+
return -1;
287289
}
288290

289291
static inline int needs_hiding(const char *path)
@@ -1798,20 +1800,8 @@ int mingw_rename(const char *pold, const char *pnew)
17981800
SetFileAttributesW(wpnew, attrs);
17991801
}
18001802
}
1801-
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
1802-
/*
1803-
* We assume that some other process had the source or
1804-
* destination file open at the wrong moment and retry.
1805-
* In order to give the other process a higher chance to
1806-
* complete its operation, we give up our time slice now.
1807-
* If we have to retry again, we do sleep a bit.
1808-
*/
1809-
Sleep(delay[tries]);
1810-
tries++;
1811-
goto repeat;
1812-
}
18131803
if (gle == ERROR_ACCESS_DENIED &&
1814-
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
1804+
retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
18151805
"Should I try again?", pold, pnew))
18161806
goto repeat;
18171807

0 commit comments

Comments
 (0)