diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:29:28 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:29:28 +0200 |
commit | 75c07011b7c4d06acd7b45dabdcd60ab9d80f385 (patch) | |
tree | 8853c3978dd152ef377e652239448b1352320206 /RealtimeSync/monitor.cpp | |
parent | 5.22 (diff) | |
download | FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.tar.gz FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.tar.bz2 FreeFileSync-75c07011b7c4d06acd7b45dabdcd60ab9d80f385.zip |
5.23
Diffstat (limited to 'RealtimeSync/monitor.cpp')
-rw-r--r-- | RealtimeSync/monitor.cpp | 281 |
1 files changed, 0 insertions, 281 deletions
diff --git a/RealtimeSync/monitor.cpp b/RealtimeSync/monitor.cpp deleted file mode 100644 index 88536281..00000000 --- a/RealtimeSync/monitor.cpp +++ /dev/null @@ -1,281 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "monitor.h" -#include <ctime> -#include <set> -#include <zen/file_handling.h> -#include <zen/dir_watcher.h> -#include <zen/thread.h> -#include <zen/tick_count.h> -#include <wx/utils.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 - -using namespace zen; - - -namespace -{ -const int CHECK_DIR_INTERVAL = 1; //unit: [s] - - -std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& dirs) //throw FileError -{ - std::set<Zstring, LessFilename> tmp; //make unique - - std::transform(dirs.begin(), dirs.end(), std::inserter(tmp, tmp.end()), - [](const Zstring& dirnameNonFmt) { return getFormattedDirectoryName(dirnameNonFmt); }); - - return std::vector<Zstring>(tmp.begin(), tmp.end()); -} - - -//wait until changes are detected or if a directory is not available (anymore) -struct WaitResult -{ - enum ChangeType - { - CHANGE_DETECTED, - CHANGE_DIR_MISSING - }; - - WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {} - WaitResult(const Zstring& dirname) : type(CHANGE_DIR_MISSING), dirname_(dirname) {} - - ChangeType type; - zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory - Zstring dirname_; //for type == CHANGE_DIR_MISSING -}; - - -WaitResult waitForChanges(const std::vector<Zstring>& dirNamesNonFmt, //throw FileError - const std::function<void(bool)>& onRefreshGui) //bool: readyForSync -{ - const std::vector<Zstring> dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError - if (dirNamesFmt.empty()) //pathological case, but we have to check else this function will wait endlessly - throw zen::FileError(_("A folder 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; - - for (auto it = dirNamesFmt.begin(); it != dirNamesFmt.end(); ++it) - { - const Zstring& dirnameFmt = *it; - try - { - //a non-existent network path may block, so check existence asynchronously! - auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); }); - //we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!) - while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) - onRefreshGui(false); //may throw! - if (!ftDirExists.get()) - return WaitResult(dirnameFmt); - - watches.push_back(std::make_pair(dirnameFmt, std::make_shared<DirWatcher>(dirnameFmt))); //throw FileError - } - catch (FileError&) - { - if (!somethingExists(dirnameFmt)) //a benign(?) race condition with FileError - return WaitResult(dirnameFmt); - throw; - } - } - - const std::int64_t TICKS_DIR_CHECK_INTERVAL = CHECK_DIR_INTERVAL * ticksPerSec(); //0 on error - TickVal lastCheck = getTicks(); //0 on error - while (true) - { - const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice - { - const TickVal now = getTicks(); //0 on error - if (dist(lastCheck, now) >= TICKS_DIR_CHECK_INTERVAL) - { - lastCheck = now; - return true; - } - return false; - }(); - - - for (auto it = watches.begin(); it != watches.end(); ++it) - { - const Zstring& dirname = it->first; - DirWatcher& watcher = *(it->second); - - //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 -> somethingExists() is NOT sufficient here! - return WaitResult(dirname); - try - { - std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { onRefreshGui(false); /*may throw!*/ }); //throw FileError - - //remove to be ignored changes - vector_remove_if(changedItems, [](const DirWatcher::Entry& e) - { - return endsWith(e.filename_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock - endsWith(e.filename_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db - //no need to ignore temporal recycle bin directory: this must be caused by a file deletion anyway - }); - - if (!changedItems.empty()) - { - /* - std::for_each(changedItems.begin(), changedItems.end(), - [](const Zstring& fn) { wxMessageBox(toWx(fn));}); - */ - return WaitResult(changedItems[0]); //directory change detected - } - - } - catch (FileError&) - { - if (!somethingExists(dirname)) //a benign(?) race condition with FileError - return WaitResult(dirname); - throw; - } - } - - boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)); - onRefreshGui(true); //throw ?: may start sync at this presumably idle time - } -} - - -//wait until all directories become available (again) + logs in network share -void waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, //throw FileError - const std::function<void(const Zstring&)>& onRefreshGui) //Zstring: the directory that is currently being waited for -{ - while (true) - { - //support specifying volume by name => call getFormattedDirectoryName() repeatedly - const std::vector<Zstring>& dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError - - bool allExisting = true; - for (auto it = dirNamesFmt.begin(); it != dirNamesFmt.end(); ++it) - { - const Zstring dirnameFmt = *it; - auto ftDirExisting = async([=]() -> bool - { -#ifdef ZEN_WIN - //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); - }); - while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2))) - onRefreshGui(dirnameFmt); //may throw! - - if (!ftDirExisting.get()) - { - allExisting = false; - //wait some time... - const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2; - assert_static(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0); - for (int i = 0; i < CHECK_DIR_INTERVAL * 1000 / refreshInterval; ++i) - { - onRefreshGui(dirnameFmt); //may throw! - boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval)); - } - break; - } - } - if (allExisting) - return; - } -} - - -inline -wxString toString(DirWatcher::ActionType type) -{ - switch (type) - { - case DirWatcher::ACTION_CREATE: - return L"CREATE"; - case DirWatcher::ACTION_UPDATE: - return L"UPDATE"; - case DirWatcher::ACTION_DELETE: - return L"DELETE"; - } - return L"ERROR"; -} - -struct ExecCommandNowException {}; -} - - -void rts::monitorDirectories(const std::vector<Zstring>& dirNamesNonFmt, unsigned int delay, rts::MonitorCallback& callback) -{ - if (dirNamesNonFmt.empty()) - { - assert(false); - return; - } - - auto execMonitoring = [&] //throw FileError - { - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); - - //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include) - time_t nextExecDate = std::time(nullptr) + delay; - - while (true) //loop over command invocations - { - DirWatcher::Entry lastChangeDetected; - try - { - while (true) //loop over detected changes - { - //wait for changes (and for all directories to become available) - WaitResult res = waitForChanges(dirNamesNonFmt, [&](bool readyForSync) //throw FileError, ExecCommandNowException - { - if (readyForSync) - if (nextExecDate <= std::time(nullptr)) - throw ExecCommandNowException(); //abort wait and start sync - callback.requestUiRefresh(); - }); - switch (res.type) - { - case WaitResult::CHANGE_DIR_MISSING: //don't execute the command before all directories are available! - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); - break; - - case WaitResult::CHANGE_DETECTED: - lastChangeDetected = res.changedItem_; - break; - } - nextExecDate = std::time(nullptr) + delay; - } - } - catch (ExecCommandNowException&) {} - - ::wxSetEnv(L"change_path", utfCvrtTo<wxString>(lastChangeDetected.filename_)); //some way to output what file changed to the user - ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); // - - //execute command - callback.executeExternalCommand(); - nextExecDate = std::numeric_limits<time_t>::max(); - } - }; - - while (true) - try - { - execMonitoring(); //throw FileError - } - catch (const zen::FileError& e) - { - callback.reportError(e.toString()); - } -} |