Commit 38541d69 authored by Kenton Varda's avatar Kenton Varda

Adjust win32 recursive delete logic.

Now that I know the actual reasoning behind the loop, I think it's not the best answer. Instead, we should retry removing the directory if it reports not-empty, but only a few times before we give up.
parent 609ea777
......@@ -142,7 +142,6 @@ private:
auto glob = join16(path, L"\\*");
for (;;) {
WIN32_FIND_DATAW data;
HANDLE handle = FindFirstFileW(glob.begin(), &data);
if (handle == INVALID_HANDLE_VALUE) {
......@@ -152,7 +151,6 @@ private:
}
KJ_DEFER(KJ_WIN32(FindClose(handle)) { break; });
bool foundAny = false;
do {
// Ignore "." and "..", ugh.
if (data.cFileName[0] == L'.') {
......@@ -179,12 +177,18 @@ private:
KJ_FAIL_WIN32("FindNextFile", error, path) { return; }
}
if (!foundAny) {
return;
uint retryCount = 0;
retry:
KJ_WIN32_HANDLE_ERRORS(RemoveDirectoryW(path.begin())) {
case ERROR_DIR_NOT_EMPTY:
if (retryCount++ < 10) {
Sleep(10);
goto retry;
}
// fallthrough
default:
KJ_FAIL_WIN32("RemoveDirectory", error) { break; }
}
KJ_WIN32(RemoveDirectoryW(path.begin()));
}
};
......
......@@ -151,13 +151,6 @@ static String dbgStr(ArrayPtr<const wchar_t> wstr) {
static void rmrfChildren(ArrayPtr<const wchar_t> path) {
auto glob = join16(path, L"*");
// According to Niall Douglas, on Windows, deleting all files in a directory requires repeatedly
// scanning the directory until it is found to be empty. I couldn't find an explanation why, but
// my guess is that deleting a file while a search is in progress could reorder the remaining
// files causing the iterator to possibly skip things?
//
// Anyway, hence the loop.
for (;;) {
WIN32_FIND_DATAW data;
HANDLE handle = FindFirstFileW(glob.begin(), &data);
if (handle == INVALID_HANDLE_VALUE) {
......@@ -167,7 +160,6 @@ static void rmrfChildren(ArrayPtr<const wchar_t> path) {
}
KJ_DEFER(KJ_WIN32(FindClose(handle)) { break; });
bool foundAny = false;
do {
// Ignore "." and "..", ugh.
if (data.cFileName[0] == L'.') {
......@@ -177,7 +169,6 @@ static void rmrfChildren(ArrayPtr<const wchar_t> path) {
}
}
foundAny = true;
auto child = join16(path, data.cFileName);
// For rmrf purposes, we assume any "reparse points" are symlink-like, even if they aren't
// actually the "symbolic link" reparse type, because we don't want to recursively delete any
......@@ -185,7 +176,25 @@ static void rmrfChildren(ArrayPtr<const wchar_t> path) {
if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
rmrfChildren(child);
KJ_WIN32(RemoveDirectoryW(child.begin()));
uint retryCount = 0;
retry:
KJ_WIN32_HANDLE_ERRORS(RemoveDirectoryW(child.begin())) {
case ERROR_DIR_NOT_EMPTY:
// On Windows, deleting a file actually only schedules it for deletion. Under heavy
// load it may take a bit for the deletion to go through. Or, if another process has
// the file open, it may not be deleted until that process closes it.
//
// We'll repeatedly retry for up to 100ms, then give up. This is awful but there's no
// way to tell for sure if the system is just being slow or if someone has the file
// open.
if (retryCount++ < 10) {
Sleep(10);
goto retry;
}
// fallthrough
default:
KJ_FAIL_WIN32("RemoveDirectory", error, dbgStr(child)) { break; }
}
} else {
KJ_WIN32(DeleteFileW(child.begin()));
}
......@@ -195,11 +204,6 @@ static void rmrfChildren(ArrayPtr<const wchar_t> path) {
if (error != ERROR_NO_MORE_FILES) {
KJ_FAIL_WIN32("FindNextFile", error, dbgStr(path)) { return; }
}
if (!foundAny) {
return;
}
}
}
static bool rmrf(ArrayPtr<const wchar_t> path) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment