From 4c96313ed06c1313c802458b6f41eea48f852dab Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 3 Sep 2018 21:11:10 +0300 Subject: Keep trying to move filesystem entry for a second on Windows The thinking is that there can be some Windows process analyzing newly created files and so preventing their move or removal. --- libbutl/filesystem.cxx | 52 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) (limited to 'libbutl/filesystem.cxx') diff --git a/libbutl/filesystem.cxx b/libbutl/filesystem.cxx index 0e1cda5..0dfec0f 100644 --- a/libbutl/filesystem.cxx +++ b/libbutl/filesystem.cxx @@ -780,22 +780,46 @@ namespace butl DWORD mfl (fd ? 0 : (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)); - if (MoveFileExA (f, t, mfl)) - return; - - // If the destination already exists, then MoveFileExA() succeeds only if - // it is a regular file or a symlink. Lets also support an empty directory - // special case to comply with POSIX. If the destination is an empty - // directory we will just remove it and retry the move operation. + // For reasons unknown an attempt to move a file sometimes ends up with + // the 'file is being used by another process' error. If that's the case, + // we will keep trying to move the file during a second. // - // Note that under Wine we endup with ERROR_ACCESS_DENIED error code in - // that case, and with ERROR_ALREADY_EXISTS when run natively. + // The thinking is that there can be some Windows process analyzing newly + // created files and so preventing their move or removal. // - DWORD ec (GetLastError ()); - if ((ec == ERROR_ALREADY_EXISTS || ec == ERROR_ACCESS_DENIED) && td && - try_rmdir (path_cast (to)) != rmdir_status::not_empty && - MoveFileExA (f, t, mfl)) - return; + DWORD ec; + for (size_t i (0); i < 11; ++i) + { + // Sleep 100 milliseconds before the move retry. + // + if (i != 0) + Sleep (100); + + if (MoveFileExA (f, t, mfl)) + return; + + ec = GetLastError (); + + // If the destination already exists, then MoveFileExA() succeeds only + // if it is a regular file or a symlink. Lets also support an empty + // directory special case to comply with POSIX. If the destination is an + // empty directory we will just remove it and retry the move operation. + // + // Note that under Wine we endup with ERROR_ACCESS_DENIED error code in + // that case, and with ERROR_ALREADY_EXISTS when run natively. + // + if ((ec == ERROR_ALREADY_EXISTS || ec == ERROR_ACCESS_DENIED) && td && + try_rmdir (path_cast (to)) != rmdir_status::not_empty) + { + if (MoveFileExA (f, t, mfl)) + return; + + ec = GetLastError (); + } + + if (ec != ERROR_SHARING_VIOLATION) + break; + } throw_system_error (ec); -- cgit v1.1