diff --git a/far/changelog b/far/changelog index dc0a0eccb8..aaaf565738 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,9 @@ +drkns 04.07.2017 23:44:29 +0000 - build 4984 + +1. Автообновление панелей не работало в каталогах с нетрадиционными именами. + +2. Связанный с п. 1 рефакторинг filesystemwatcher, ибо как-то всё весьма мутно было. + drkns 03.07.2017 19:31:28 +0000 - build 4983 1. В некоторых случаях макрос мог вызвать исключение в меню со включенным фильтром. diff --git a/far/farwinapi.cpp b/far/farwinapi.cpp index 70828bdd73..b2f2a900b0 100644 --- a/far/farwinapi.cpp +++ b/far/farwinapi.cpp @@ -1850,6 +1850,11 @@ bool GetDefaultPrinter(string& Printer) }); } +find_notification_handle FindFirstChangeNotification(const string& PathName, bool WatchSubtree, DWORD NotifyFilter) +{ + return find_notification_handle(::FindFirstChangeNotification(NTPath(PathName).data(), WatchSubtree, NotifyFilter)); +} + handle OpenCurrentThread() { HANDLE Handle; diff --git a/far/farwinapi.hpp b/far/farwinapi.hpp index daed96e189..4e36222289 100644 --- a/far/farwinapi.hpp +++ b/far/farwinapi.hpp @@ -156,6 +156,7 @@ namespace os bool IsWow64Process(); DWORD GetAppPathsRedirectionFlag(); bool GetDefaultPrinter(string& Printer); + find_notification_handle FindFirstChangeNotification(const string& PathName, bool WatchSubtree, DWORD NotifyFilter); bool CreateSymbolicLinkInternal(const string& Object, const string& Target, DWORD dwFlags); bool SetFileEncryptionInternal(const wchar_t* Name, bool Encrypt); diff --git a/far/filesystemwatcher.cpp b/far/filesystemwatcher.cpp index f05d43d3a9..d8f1ab04ea 100644 --- a/far/filesystemwatcher.cpp +++ b/far/filesystemwatcher.cpp @@ -37,113 +37,144 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "flink.hpp" #include "elevation.hpp" #include "datetime.hpp" +#include "farexcpt.hpp" +#include "pathmix.hpp" FileSystemWatcher::FileSystemWatcher(): m_PreviousLastWriteTime(), m_CurrentLastWriteTime(), - m_bOpen(false), m_WatchSubtree(false), - m_WatchRegistered(os::event::type::manual, os::event::state::signaled), - m_Done(os::event::type::manual, os::event::state::nonsignaled), - m_DoneDone(os::event::type::manual, os::event::state::nonsignaled), - m_Changed(os::event::type::manual, os::event::state::nonsignaled) + m_Cancelled(os::event::type::manual, os::event::state::nonsignaled) { } FileSystemWatcher::~FileSystemWatcher() { - Release(); + try + { + Release(); + } + catch(...) + { + // TODO: log + } } void FileSystemWatcher::Set(const string& Directory, bool WatchSubtree) { - m_WatchRegistered.wait(); - m_Directory = Directory; + Release(); + + m_Directory = NTPath(Directory); m_WatchSubtree = WatchSubtree; if (os::GetFileTimeSimple(Directory,nullptr,nullptr,&m_PreviousLastWriteTime,nullptr)) m_CurrentLastWriteTime = m_PreviousLastWriteTime; -} -void FileSystemWatcher::WatchRegister() const -{ - // TODO: SEH guard, try/catch, exception_ptr - os::find_notification_handle Handle(FindFirstChangeNotification(m_Directory.data(), m_WatchSubtree, - FILE_NOTIFY_CHANGE_FILE_NAME| - FILE_NOTIFY_CHANGE_DIR_NAME| - FILE_NOTIFY_CHANGE_ATTRIBUTES| - FILE_NOTIFY_CHANGE_SIZE| - FILE_NOTIFY_CHANGE_LAST_WRITE)); - m_WatchRegistered.set(); - - os::multi_waiter waiter; - waiter.add(Handle.native_handle()); - waiter.add(m_Done); - if (waiter.wait(os::multi_waiter::mode::any) == WAIT_OBJECT_0) - { - m_Changed.set(); - m_Done.wait(); - } - - m_DoneDone.set(); + m_IsFatFilesystem = {}; } void FileSystemWatcher::Watch(bool got_focus, bool check_time) { + PropagateException(); + SCOPED_ACTION(elevation::suppress); - if(!m_bOpen) - { - m_bOpen = true; - m_Done.reset(); - m_DoneDone.reset(); - m_WatchRegistered.reset(); - os::thread(&os::thread::detach, &FileSystemWatcher::WatchRegister, this); - } + if(!m_RegistrationThread) + m_RegistrationThread = os::thread(&os::thread::join, &FileSystemWatcher::Register, this); if (got_focus) { - bool isFAT = false; - const auto strRoot = GetPathRoot(m_Directory); - if (!strRoot.empty()) + if (!m_IsFatFilesystem.second) { - string strFileSystem; - if (os::GetVolumeInformation(strRoot, nullptr, nullptr, nullptr, nullptr, &strFileSystem)) - isFAT = !strFileSystem.compare(0, 3, L"FAT", 3); + const auto strRoot = GetPathRoot(m_Directory); + if (!strRoot.empty()) + { + string strFileSystem; + if (os::GetVolumeInformation(strRoot, nullptr, nullptr, nullptr, nullptr, &strFileSystem)) + m_IsFatFilesystem.first = !strFileSystem.compare(0, 3, L"FAT", 3); + } + + m_IsFatFilesystem.second = true; } - if (isFAT) // emulate FAT folder time change - { // otherwise changes missed (FAT folder time is NOT modified) - check_time = false; // the price is directory reload on each GOT_FOCUS event + + if (m_IsFatFilesystem.first) + { + // emulate FAT folder time change + // otherwise changes missed (FAT folder time is NOT modified) + // the price is directory reload on each GOT_FOCUS event + check_time = false; m_PreviousLastWriteTime.dwLowDateTime = m_CurrentLastWriteTime.dwLowDateTime - 1; } } if (check_time) { - if (!os::GetFileTimeSimple(m_Directory,nullptr,nullptr,&m_CurrentLastWriteTime,nullptr)) + if (!os::GetFileTimeSimple(m_Directory, nullptr, nullptr, &m_CurrentLastWriteTime, nullptr)) { - m_PreviousLastWriteTime.dwLowDateTime = 0; - m_PreviousLastWriteTime.dwHighDateTime = 0; - m_CurrentLastWriteTime.dwLowDateTime = 0; - m_CurrentLastWriteTime.dwHighDateTime = 0; + m_PreviousLastWriteTime = {}; + m_CurrentLastWriteTime = {}; } } } void FileSystemWatcher::Release() { - if (m_bOpen) + PropagateException(); + + if (m_RegistrationThread) { - m_Done.set(); - m_DoneDone.wait(); - m_bOpen = false; - m_Changed.reset(); + m_Cancelled.set(); + m_RegistrationThread.reset(); } + + m_Cancelled.reset(); + m_Notification.reset(); m_PreviousLastWriteTime = m_CurrentLastWriteTime; } bool FileSystemWatcher::Signaled() const { - return m_Changed.is_signaled() || m_PreviousLastWriteTime != m_CurrentLastWriteTime; + PropagateException(); + + return m_Notification.is_signaled() || m_PreviousLastWriteTime != m_CurrentLastWriteTime; +} + +void FileSystemWatcher::Register() +{ + seh_invoke_thread(m_ExceptionPtr, [this] + { + try + { + m_Notification = os::FindFirstChangeNotification(m_Directory, m_WatchSubtree, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE); + + if (!m_Notification) + return; + + os::multi_waiter waiter; + waiter.add(m_Notification.native_handle()); + waiter.add(m_Cancelled); + waiter.wait(os::multi_waiter::mode::any); + } + catch (...) + { + m_ExceptionPtr = std::current_exception(); + m_IsRegularException = true; + } + }); +} + +void FileSystemWatcher::PropagateException() const +{ + if (m_ExceptionPtr && !m_IsRegularException) + { + // You're someone else's problem + m_RegistrationThread.detach(); + } + RethrowIfNeeded(m_ExceptionPtr); } diff --git a/far/filesystemwatcher.hpp b/far/filesystemwatcher.hpp index c1207420d5..cc927125ce 100644 --- a/far/filesystemwatcher.hpp +++ b/far/filesystemwatcher.hpp @@ -47,17 +47,20 @@ class FileSystemWatcher: noncopyable bool Signaled() const; private: - void WatchRegister() const; + void Register(); + void PropagateException() const; string m_Directory; FILETIME m_PreviousLastWriteTime; FILETIME m_CurrentLastWriteTime; - bool m_bOpen; bool m_WatchSubtree; - os::event m_WatchRegistered; - os::event m_Done; - os::event m_DoneDone; - os::event m_Changed; + mutable os::thread m_RegistrationThread; + os::find_notification_handle m_Notification; + os::event m_Cancelled; + // TODO: optional + std::pair m_IsFatFilesystem; + mutable std::exception_ptr m_ExceptionPtr; + bool m_IsRegularException{}; }; #endif // FILESYSTEMWATCHER_HPP_A4DC2834_A694_4E86_B8BA_FDA8DBF728CD diff --git a/far/vbuild.m4 b/far/vbuild.m4 index c25e753d91..e8c587af01 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -m4_define(BUILD,4983)m4_dnl +m4_define(BUILD,4984)m4_dnl