Skip to content

Commit

Permalink
filesystemwatcher fix for non-conforming names & refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
alabuzhev committed Jul 4, 2017
1 parent b94b378 commit ca73ada
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 65 deletions.
6 changes: 6 additions & 0 deletions 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. В некоторых случаях макрос мог вызвать исключение в меню со включенным фильтром.
Expand Down
5 changes: 5 additions & 0 deletions far/farwinapi.cpp
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions far/farwinapi.hpp
Expand Up @@ -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);
Expand Down
147 changes: 89 additions & 58 deletions far/filesystemwatcher.cpp
Expand Up @@ -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);
}
15 changes: 9 additions & 6 deletions far/filesystemwatcher.hpp
Expand Up @@ -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<bool, bool> m_IsFatFilesystem;
mutable std::exception_ptr m_ExceptionPtr;
bool m_IsRegularException{};
};

#endif // FILESYSTEMWATCHER_HPP_A4DC2834_A694_4E86_B8BA_FDA8DBF728CD
2 changes: 1 addition & 1 deletion far/vbuild.m4
@@ -1 +1 @@
m4_define(BUILD,4983)m4_dnl
m4_define(BUILD,4984)m4_dnl

0 comments on commit ca73ada

Please sign in to comment.