summaryrefslogtreecommitdiff
path: root/RealtimeSync/watcher.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:19:49 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:19:49 +0200
commitc8e0e909b4a8d18319fc65434a10dc446434817c (patch)
treeeee91e7d2ce229dd043811eae8f1e2bd78061916 /RealtimeSync/watcher.cpp
parent5.2 (diff)
downloadFreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.gz
FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.tar.bz2
FreeFileSync-c8e0e909b4a8d18319fc65434a10dc446434817c.zip
5.3
Diffstat (limited to 'RealtimeSync/watcher.cpp')
-rw-r--r--RealtimeSync/watcher.cpp138
1 files changed, 71 insertions, 67 deletions
diff --git a/RealtimeSync/watcher.cpp b/RealtimeSync/watcher.cpp
index 36ceb006..ee56bc7c 100644
--- a/RealtimeSync/watcher.cpp
+++ b/RealtimeSync/watcher.cpp
@@ -5,30 +5,31 @@
// **************************************************************************
#include "watcher.h"
+#include <set>
+#include <zen/tick_count.h>
#include <zen/file_handling.h>
#include <zen/stl_tools.h>
-#include <set>
-#include <ctime>
-#include <wx/timer.h>
-#include "../lib/resolve_path.h"
#include <zen/dir_watcher.h>
-#include <wx+/string_conv.h>
#include <zen/thread.h>
+#include <zen/assert_static.h>
+#include <wx+/string_conv.h>
+#include <wx/timer.h>
+#include "../lib/resolve_path.h"
//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header
//#include "../library/lock_holder.h" //LOCK_FILE_ENDING
-#include <wx/msgdlg.h>
using namespace zen;
+namespace
+{
+const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
+TickVal lastExec = getTicks();
+};
bool rts::updateUiIsAllowed()
{
- const std::clock_t CLOCK_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * CLOCKS_PER_SEC / 1000;
-
- static std::clock_t lastExec = 0;
- const std::clock_t now = std::clock(); //this is quite fast: 2 * 10^-5
-
- if (now - lastExec >= CLOCK_UPDATE_INTERVAL) //perform ui updates not more often than necessary
+ const TickVal now = getTicks(); //0 on error
+ if (now - lastExec >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
{
lastExec = now;
return true;
@@ -36,29 +37,30 @@ bool rts::updateUiIsAllowed()
return false;
}
+
namespace
{
const int CHECK_DIR_INTERVAL = 1000; //1 second interval
-}
-rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback* statusHandler) //throw FileError
+std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& dirs) //throw FileError
{
- std::set<Zstring, LessFilename> dirNamesFmt;
+ std::set<Zstring, LessFilename> tmp; //make unique
- std::for_each(dirNamesNonFmt.begin(), dirNamesNonFmt.end(),
- [&](const Zstring& dirnameNonFmt)
- {
- const Zstring& dirnameFmt = zen::getFormattedDirectoryName(dirnameNonFmt);
+ std::transform(dirs.begin(), dirs.end(), std::inserter(tmp, tmp.end()),
+ [](const Zstring& dirnameNonFmt) { return getFormattedDirectoryName(dirnameNonFmt); });
- if (dirnameFmt.empty())
- throw zen::FileError(_("A directory input field is empty."));
- dirNamesFmt.insert(dirnameFmt);
- });
- if (dirNamesFmt.empty()) //pathological case, but check is needed nevertheless
- throw zen::FileError(_("A directory input field is empty."));
+ return std::vector<Zstring>(tmp.begin(), tmp.end());
+}
+}
+rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback& statusHandler) //throw FileError
+{
+ const std::vector<Zstring> dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError
+ if (dirNamesFmt.empty()) //pathological case, but check is needed nevertheless
+ throw zen::FileError(_("A directory input field is empty.")); //should have been checked by caller!
+
//detect when volumes are removed/are not available anymore
std::vector<std::pair<Zstring, std::shared_ptr<DirWatcher>>> watches;
@@ -67,16 +69,24 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
const Zstring& dirnameFmt = *iter;
try
{
+ //a non-existent network path may block, so check existence asynchronously!
+ auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); });
+ while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL)))
+ statusHandler.requestUiRefresh(); //may throw!
+ if (!ftDirExists.get())
+ return WaitResult(CHANGE_DIR_MISSING, dirnameFmt);
+
+
watches.push_back(std::make_pair(dirnameFmt, std::make_shared<DirWatcher>(dirnameFmt))); //throw FileError, ErrorNotExisting
}
catch (ErrorNotExisting&) //nice atomic behavior: *no* second directory existence check!!!
{
- return CHANGE_DIR_MISSING;
+ return WaitResult(CHANGE_DIR_MISSING, dirnameFmt);
}
catch (FileError&) //play safe: remedy potential FileErrors that should have been ErrorNotExisting (e.g. Linux: errors during directory traversing)
{
- if (!dirExists(dirnameFmt)) //not an atomic behavior!!!
- return CHANGE_DIR_MISSING;
+ if (!dirExists(dirnameFmt)) //file system race condition!!
+ return WaitResult(CHANGE_DIR_MISSING, dirnameFmt);
throw;
}
}
@@ -104,11 +114,11 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
//IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories!
if (checkDirExistNow)
if (!dirExists(dirname)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED
- return CHANGE_DIR_MISSING;
+ return WaitResult(CHANGE_DIR_MISSING, dirname);
try
{
- std::vector<Zstring> changedFiles = watcher.getChanges([&] { statusHandler->requestUiRefresh(); }); //throw FileError, ErrorNotExisting
+ std::vector<Zstring> changedFiles = watcher.getChanges([&] { statusHandler.requestUiRefresh(); }); //throw FileError, ErrorNotExisting
//remove to be ignored changes
vector_remove_if(changedFiles, [](const Zstring& name)
@@ -130,67 +140,61 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
}
catch (ErrorNotExisting&) //nice atomic behavior: *no* second directory existence check!!!
{
- return CHANGE_DIR_MISSING;
+ return WaitResult(CHANGE_DIR_MISSING, dirname);
}
catch (FileError&) //play safe: remedy potential FileErrors that should have been ErrorNotExisting (e.g. Linux: errors during directory traversing)
{
- if (!dirExists(dirname)) //not an atomic behavior!!!
- return CHANGE_DIR_MISSING;
+ if (!dirExists(dirname)) //file system race condition!!
+ return WaitResult(CHANGE_DIR_MISSING, dirname);
throw;
}
}
- wxMilliSleep(rts::UI_UPDATE_INTERVAL);
- statusHandler->requestUiRefresh();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL));
+ statusHandler.requestUiRefresh();
}
}
//support for monitoring newly connected directories volumes (e.g.: USB-sticks)
-void rts::waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback* statusHandler) //throw FileError
+void rts::waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback& statusHandler) //throw FileError
{
- wxLongLong lastCheck;
-
while (true)
{
- const wxLongLong current = wxGetLocalTimeMillis();
- if (current - lastCheck >= CHECK_DIR_INTERVAL)
- {
- lastCheck = current;
+ //support specifying volume by name => call getFormattedDirectoryName() repeatedly
+ const std::vector<Zstring>& dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError
- auto ftDirMissing = async([=]() -> bool
+ bool allExisting = true;
+ for (auto iter = dirNamesFmt.begin(); iter != dirNamesFmt.end(); ++iter)
+ {
+ const Zstring dirnameFmt = *iter;
+ auto ftDirExisting = async([=]() -> bool
{
- return std::find_if(dirNamesNonFmt.begin(), dirNamesNonFmt.end(),
- [](const Zstring& dirnameNonFmt) -> bool
- {
- //support specifying volume by name => call getFormattedDirectoryName() repeatedly
- const Zstring dirnameFmt = zen::getFormattedDirectoryName(dirnameNonFmt);
-
- if (dirnameFmt.empty())
- throw zen::FileError(_("A directory input field is empty."));
#ifdef FFS_WIN
- //1. login to network share, if necessary
- loginNetworkShare(dirnameFmt, false); //login networks shares, no PW prompt -> is this really RTS's task?
+ //1. login to network share, if necessary -> we probably do NOT want multiple concurrent runs: GUI!?
+ loginNetworkShare(dirnameFmt, false); //login networks shares, no PW prompt -> is this really RTS's job?
#endif
- //2. check dir existence
- return !zen::dirExists(dirnameFmt);
- }) != dirNamesNonFmt.end();
+ //2. check dir existence
+ return zen::dirExists(dirnameFmt);
});
- while (!ftDirMissing.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL)))
- statusHandler->requestUiRefresh(); //may throw!
+ while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL)))
+ statusHandler.requestUiRefresh(); //may throw!
- try
+ if (!ftDirExisting.get())
{
- if (!ftDirMissing.get()) //throw X
- return;
- }
- catch (...) //boost::future seems to map async exceptions to "some" boost exception type -> migrate this for C++11
- {
- throw zen::FileError(_("A directory input field is empty."));
+ allExisting = false;
+ break;
}
}
+ if (allExisting)
+ return;
- wxMilliSleep(rts::UI_UPDATE_INTERVAL);
- statusHandler->requestUiRefresh();
+ //wait some time...
+ assert_static(CHECK_DIR_INTERVAL % UI_UPDATE_INTERVAL == 0);
+ for (int i = 0; i < CHECK_DIR_INTERVAL / UI_UPDATE_INTERVAL; ++i)
+ {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL));
+ statusHandler.requestUiRefresh();
+ }
}
}
bgstack15