diff --git a/.ecode/project_build.json b/.ecode/project_build.json index b07e874..815d421 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -31,7 +31,16 @@ "preset": "generic", "relative_file_paths": true } - } + }, + "run": [ + { + "args": "", + "command": "efsw-test-debug", + "name": "efsw-test-debug", + "run_in_terminal": true, + "working_dir": "${project_root}/bin" + } + ] }, "linux-release": { "build": [ diff --git a/include/efsw/efsw.h b/include/efsw/efsw.h index fc9a3a3..30cf595 100644 --- a/include/efsw/efsw.h +++ b/include/efsw/efsw.h @@ -106,6 +106,13 @@ enum efsw_option { /// the number of events reported. This will have an small performance and memory impact as a /// consequence. EFSW_OPT_MAC_SANITIZE_EVENTS = 4, + /// Linux does not support natively recursive watchers. This means that when using recursive + /// watches efsw registers new watchers for each directory. If new file are created between + /// the time efsw takes to register the new directory those events might be missed. To avoid + /// missing new file notifications efsw will trigger synthetic new file events for existing + /// files in the new directroy watched. This might have the unintended consequence of sending + /// duplicated created events due to the system also emitting this event. + LINUX_PRODUCE_SYNTHETIC_EVENTS = 5, }; /// Basic interface for listening for file events. diff --git a/include/efsw/efsw.hpp b/include/efsw/efsw.hpp index 3c0bb2b..11a5dec 100644 --- a/include/efsw/efsw.hpp +++ b/include/efsw/efsw.hpp @@ -148,6 +148,13 @@ enum Option { /// the number of events reported. This will have an small performance and memory impact as a /// consequence. MacSanitizeEvents = 4, + /// Linux does not support natively recursive watchers. This means that when using recursive + /// watches efsw registers new watchers for each directory. If new file are created between + /// the time efsw takes to register the new directory those events might be missed. To avoid + /// missing new file notifications efsw will trigger synthetic created file events for existing + /// files in the new directroy watched. This might have the unintended consequence of sending + /// duplicated created events due to the system also emitting this event. + LinuxProduceSyntheticEvents = 5, }; } typedef Options::Option Option; diff --git a/src/efsw/FileWatcherInotify.cpp b/src/efsw/FileWatcherInotify.cpp index 29be12b..1ec3d48 100644 --- a/src/efsw/FileWatcherInotify.cpp +++ b/src/efsw/FileWatcherInotify.cpp @@ -69,15 +69,17 @@ FileWatcherInotify::~FileWatcherInotify() { } WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive, const std::vector& ) { + bool recursive, const std::vector& options ) { if ( !mInitOK ) return Errors::Log::createLastError( Errors::Unspecified, directory ); Lock initLock( mInitLock ); - return addWatch( directory, watcher, recursive, NULL ); + bool syntheticEvents = getOptionValue( options, Options::LinuxProduceSyntheticEvents, 0 ) != 0; + return addWatch( directory, watcher, recursive, syntheticEvents, NULL ); } WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive, WatcherInotify* parent ) { + bool recursive, bool syntheticEvents, + WatcherInotify* parent, bool fromInternalEvent ) { std::string dir( directory ); FileSystem::dirAddSlashAtEnd( dir ); @@ -160,7 +162,17 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis const FileInfo& cfi = it->second; if ( cfi.isDirectory() && cfi.isReadable() ) { - addWatch( cfi.Filepath, watcher, recursive, pWatch ); + addWatch( cfi.Filepath, watcher, recursive, syntheticEvents, pWatch ); + } + } + + if ( fromInternalEvent && parent != NULL && syntheticEvents ) { + for ( const auto& file : files ) { + if ( file.second.isRegularFile() ) { + pWatch->Listener->handleFileAction( + pWatch->ID, pWatch->Directory, + FileSystem::fileNameFromPath( file.second.Filepath ), Actions::Add ); + } } } } @@ -448,8 +460,9 @@ void FileWatcherInotify::checkForNewWatcher( Watcher* watch, std::string fpath ) } if ( !found ) { - addWatch( fpath, watch->Listener, watch->Recursive, - static_cast( watch ) ); + WatcherInotify* iWatch = static_cast( watch ); + addWatch( fpath, watch->Listener, watch->Recursive, iWatch->syntheticEvents, + static_cast( watch ), true ); } } } diff --git a/src/efsw/FileWatcherInotify.hpp b/src/efsw/FileWatcherInotify.hpp index 84174a0..26d2c0b 100644 --- a/src/efsw/FileWatcherInotify.hpp +++ b/src/efsw/FileWatcherInotify.hpp @@ -65,7 +65,8 @@ class FileWatcherInotify : public FileWatcherImpl { std::vector> mMovedOutsideWatches; WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, - WatcherInotify* parent = NULL ); + bool syntheticEvents, WatcherInotify* parent = NULL, + bool fromInternalEvent = false ); bool pathInWatches( const std::string& path ) override; diff --git a/src/efsw/WatcherInotify.hpp b/src/efsw/WatcherInotify.hpp index d43935c..ec55ed0 100644 --- a/src/efsw/WatcherInotify.hpp +++ b/src/efsw/WatcherInotify.hpp @@ -16,6 +16,7 @@ class WatcherInotify : public Watcher { WatchID InotifyID; FileInfo DirInfo; + bool syntheticEvents{ false }; }; } // namespace efsw