From 731936ce9ef83e42461898285e251ab270669d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 30 May 2019 11:33:04 +0200 Subject: [PATCH 1/2] Separate the polling logic of PollEventDriverWatchers into a separate class. Allows to keep thread-local state separate from the mutex-protected/immutable state in PollingThread to make sure that there are no unwanted accesses. --- source/eventcore/drivers/posix/watchers.d | 104 +++++++++++++--------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/source/eventcore/drivers/posix/watchers.d b/source/eventcore/drivers/posix/watchers.d index 67d310a..647d948 100644 --- a/source/eventcore/drivers/posix/watchers.d +++ b/source/eventcore/drivers/posix/watchers.d @@ -332,40 +332,6 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat immutable string m_basePath; immutable bool m_recursive; immutable FileChangesCallback m_callback; - - final static class Entry { - Entry parent; - string name; - ulong size; - long lastChange; - - this(Entry parent, string name, ulong size, long lastChange) - { - this.parent = parent; - this.name = name; - this.size = size; - this.lastChange = lastChange; - } - - string path() - { - import std.path : buildPath; - if (parent) - return buildPath(parent.path, name); - else return name; - } - - bool isDir() const { return size == ulong.max; } - } - - struct Key { - Entry parent; - string name; - } - - // used only within the polling thread - Entry[Key] m_entries; - size_t m_entryCount; } this(shared(Events) event_driver, EventID event, string path, bool recursive, FileChangesCallback callback) @@ -378,7 +344,6 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat m_basePath = path; m_recursive = recursive; m_callback = callback; - scan(false); try super(&run); catch (Exception e) assert(false, e.msg); @@ -407,8 +372,16 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat import core.time : MonoTime, msecs; import std.algorithm.comparison : min; + auto poller = new DirectoryPoller(m_basePath, m_recursive, (ch) { + try synchronized (m_changesMutex) { + m_changes ~= ch; + } catch (Exception e) assert(false, "Mutex lock failed: "~e.msg); + }); + + poller.scan(false); + try while (true) { - auto timeout = MonoTime.currTime() + min(m_entryCount, 60000).msecs + 1000.msecs; + auto timeout = MonoTime.currTime() + min(poller.entryCount, 60000).msecs + 1000.msecs; while (true) { try synchronized (m_changesMutex) { if (m_changesEvent == EventID.invalid) return; @@ -418,7 +391,7 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat sleep(min(1000.msecs, remaining)); } - scan(true); + poller.scan(true); try synchronized (m_changesMutex) { if (m_changesEvent == EventID.invalid) return; @@ -434,11 +407,62 @@ final class PollEventDriverWatchers(Events : EventDriverEvents) : EventDriverWat } } + } + + private final class DirectoryPoller { + private final static class Entry { + Entry parent; + string name; + ulong size; + long lastChange; + + this(Entry parent, string name, ulong size, long lastChange) + { + this.parent = parent; + this.name = name; + this.size = size; + this.lastChange = lastChange; + } + + string path() + { + import std.path : buildPath; + if (parent) + return buildPath(parent.path, name); + else return name; + } + + bool isDir() const { return size == ulong.max; } + } + + private struct Key { + Entry parent; + string name; + } + + alias ChangeCallback = void delegate(FileChange) @safe nothrow; + + private { + immutable string m_basePath; + immutable bool m_recursive; + + Entry[Key] m_entries; + size_t m_entryCount; + ChangeCallback m_onChange; + } + + this(string path, bool recursive, ChangeCallback on_change) + { + m_basePath = path; + m_recursive = recursive; + m_onChange = on_change; + } + + @property size_t entryCount() const { return m_entryCount; } + private void addChange(FileChangeKind kind, Key key, bool is_dir) { - try synchronized (m_changesMutex) { - m_changes ~= FileChange(kind, m_basePath, key.parent ? key.parent.path : "", key.name, is_dir); - } catch (Exception e) assert(false, "Mutex lock failed: "~e.msg); + m_onChange(FileChange(kind, m_basePath, key.parent ? key.parent.path : "", key.name, is_dir)); } private void scan(bool generate_changes) From 2a6488a101b7a54e114993b60a51bfed68fa7681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 1 Jun 2019 21:44:20 +0200 Subject: [PATCH 2/2] Fix test to allow the directory watcher to start up. --- tests/0-dirwatcher.d | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/0-dirwatcher.d b/tests/0-dirwatcher.d index c423b74..a7ec09a 100644 --- a/tests/0-dirwatcher.d +++ b/tests/0-dirwatcher.d @@ -50,18 +50,24 @@ void main() } }); - auto fil = File(testDir~"/"~testFilename, "wt"); + File fil; auto tm = eventDriver.timers.create(); - eventDriver.timers.set(tm, 1500.msecs, 0.msecs); + eventDriver.timers.set(tm, 500.msecs, 0.msecs); eventDriver.timers.wait(tm, (tm) { - scope (failure) assert(false); - fil.write("test"); - fil.close(); + try fil = File(testDir~"/"~testFilename, "wt"); + catch (Exception e) assert(false, e.msg); + eventDriver.timers.set(tm, 1500.msecs, 0.msecs); eventDriver.timers.wait(tm, (tm) { scope (failure) assert(false); - remove(testDir~"/"~testFilename); + fil.write("test"); + fil.close(); + eventDriver.timers.set(tm, 1500.msecs, 0.msecs); + eventDriver.timers.wait(tm, (tm) { + scope (failure) assert(false); + remove(testDir~"/"~testFilename); + }); }); });