Retry commits of file writes for up to a second to work around AV scanning

Poorly-written antivirus software briefly lock newly written files to
scan them for viruses, which makes the rename from the temp file to
actual file fail. Work around this by retrying the rename up to ten
times.

Closes #1620.
This commit is contained in:
Thomas Goyne 2013-07-02 19:49:45 -07:00
parent b77ca808d0
commit af74371f6d
5 changed files with 31 additions and 8 deletions

View file

@ -64,7 +64,18 @@ Save::Save(fs::path const& file, bool binary)
Save::~Save() { Save::~Save() {
fp->close(); // Need to close before rename on Windows to unlock the file fp->close(); // Need to close before rename on Windows to unlock the file
fs::Rename(tmp_name, file_name); for (int i = 0; i < 10; ++i) {
try {
fs::Rename(tmp_name, file_name);
return;
}
catch (agi::fs::FileSystemError const&) {
// Retry up to ten times in case it's just locked by a poorly-written antivirus scanner
if (i == 9)
throw;
util::sleep_for(100);
}
}
} }
} // namespace io } // namespace io

View file

@ -70,5 +70,10 @@ namespace agi {
} }
#endif #endif
/// A thin wrapper around this_thread::sleep_for that uses std::thread on
/// Windows (to avoid having to compile boost.thread) and boost::thread
/// elsewhere (because libstcc++ 4.7 is missing it).
void sleep_for(int ms);
} // namespace util } // namespace util
} // namespace agi } // namespace agi

View file

@ -20,6 +20,8 @@
#include "libaegisub/util.h" #include "libaegisub/util.h"
#include <boost/thread.hpp>
namespace agi { namespace util { namespace agi { namespace util {
timeval time_log() { timeval time_log() {
@ -30,4 +32,8 @@ timeval time_log() {
void SetThreadName(const char *) { } void SetThreadName(const char *) { }
void sleep_for(int ms) {
boost::this_thread::sleep_for(boost::chrono::milliseconds(ms));
}
} } } }

View file

@ -23,6 +23,8 @@
#include "libaegisub/charset_conv_win.h" #include "libaegisub/charset_conv_win.h"
#include <thread>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
@ -99,5 +101,9 @@ void SetThreadName(LPCSTR szThreadName) {
__except (EXCEPTION_CONTINUE_EXECUTION) {} __except (EXCEPTION_CONTINUE_EXECUTION) {}
} }
void sleep_for(int ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
} // namespace io } // namespace io
} // namespace agi } // namespace agi

View file

@ -50,13 +50,12 @@
#include <libaegisub/dispatch.h> #include <libaegisub/dispatch.h>
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include <libaegisub/util.h>
#include <boost/gil/gil_all.hpp> #include <boost/gil/gil_all.hpp>
#include <boost/range/algorithm_ext/push_back.hpp> #include <boost/range/algorithm_ext/push_back.hpp>
#include <boost/thread.hpp>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <thread>
namespace { namespace {
std::unique_ptr<agi::dispatch::Queue> cache_queue; std::unique_ptr<agi::dispatch::Queue> cache_queue;
@ -104,11 +103,7 @@ LibassSubtitlesProvider::LibassSubtitlesProvider(std::string)
progress.Run([=](agi::ProgressSink *ps) { progress.Run([=](agi::ProgressSink *ps) {
ps->SetIndeterminate(); ps->SetIndeterminate();
while (!*done && !ps->IsCancelled()) while (!*done && !ps->IsCancelled())
#ifdef _MSC_VER agi::util::sleep_for(250);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
#else
boost::this_thread::sleep_for(boost::chrono::milliseconds(250));
#endif
}); });
ass_renderer = *renderer; ass_renderer = *renderer;