diff options
39 files changed, 625 insertions, 752 deletions
diff --git a/dep/PackageList.txt b/dep/PackageList.txt index 10625227d45..64ee91997f4 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -10,7 +10,7 @@ Boost Process (Proposed for boost, but its not an official part of it yet. Used efsw (Entropia File System Watcher - crossplatform file system watcher) https://github.com/SpartanJ/efsw - Version: 1.3.1+ 36c1c7004a34b6f40719f0830bcfb10325415451 + Version: 1.5.0+ f94a6616aba85fc9375fdff7ee69609d223a0672 fmt (a small, safe and fast formatting library) https://github.com/fmtlib/fmt diff --git a/dep/efsw/CMakeLists.txt b/dep/efsw/CMakeLists.txt index 9817525931c..555f1b303a0 100644 --- a/dep/efsw/CMakeLists.txt +++ b/dep/efsw/CMakeLists.txt @@ -22,14 +22,12 @@ if (BUILD_SHARED_LIBS) src/efsw/FileWatcherImpl.hpp src/efsw/Lock.hpp src/efsw/Log.cpp - src/efsw/Mutex.cpp src/efsw/Mutex.hpp src/efsw/sophist.h src/efsw/String.cpp src/efsw/String.hpp src/efsw/System.cpp src/efsw/System.hpp - src/efsw/Thread.cpp src/efsw/Thread.hpp src/efsw/Utf.hpp src/efsw/Watcher.cpp @@ -43,22 +41,14 @@ if (BUILD_SHARED_LIBS) list (APPEND EFSW_CPP_SOURCE src/efsw/platform/win/FileSystemImpl.cpp src/efsw/platform/win/FileSystemImpl.hpp - src/efsw/platform/win/MutexImpl.cpp - src/efsw/platform/win/MutexImpl.hpp src/efsw/platform/win/SystemImpl.cpp - src/efsw/platform/win/SystemImpl.hpp - src/efsw/platform/win/ThreadImpl.cpp - src/efsw/platform/win/ThreadImpl.hpp) + src/efsw/platform/win/SystemImpl.hpp) else () list (APPEND EFSW_CPP_SOURCE src/efsw/platform/posix/FileSystemImpl.cpp src/efsw/platform/posix/FileSystemImpl.hpp - src/efsw/platform/posix/MutexImpl.cpp - src/efsw/platform/posix/MutexImpl.hpp src/efsw/platform/posix/SystemImpl.cpp - src/efsw/platform/posix/SystemImpl.hpp - src/efsw/platform/posix/ThreadImpl.cpp - src/efsw/platform/posix/ThreadImpl.hpp) + src/efsw/platform/posix/SystemImpl.hpp) endif() if (APPLE) diff --git a/dep/efsw/include/efsw/efsw.h b/dep/efsw/include/efsw/efsw.h index ecb9ec41c26..56f24d03452 100644 --- a/dep/efsw/include/efsw/efsw.h +++ b/dep/efsw/include/efsw/efsw.h @@ -1,7 +1,7 @@ /** @author Sepul Sepehr Taghdisian - Copyright (c) 2013 Martin Lucas Golini + Copyright (c) 2024 Martín Lucas Golini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -32,31 +32,31 @@ extern "C" { #endif -#if defined(_WIN32) - #ifdef EFSW_DYNAMIC - // Windows platforms - #ifdef EFSW_EXPORTS - // From DLL side, we must export - #define EFSW_API __declspec(dllexport) - #else - // From client application side, we must import - #define EFSW_API __declspec(dllimport) - #endif - #else - // No specific directive needed for static build - #ifndef EFSW_API - #define EFSW_API - #endif - #endif +#if defined( _WIN32 ) +#ifdef EFSW_DYNAMIC +// Windows platforms +#ifdef EFSW_EXPORTS +// From DLL side, we must export +#define EFSW_API __declspec( dllexport ) #else - #if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) - #define EFSW_API __attribute__ ((visibility("default"))) - #endif - - // Other platforms don't need to define anything - #ifndef EFSW_API - #define EFSW_API - #endif +// From client application side, we must import +#define EFSW_API __declspec( dllimport ) +#endif +#else +// No specific directive needed for static build +#ifndef EFSW_API +#define EFSW_API +#endif +#endif +#else +#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) +#define EFSW_API __attribute__( ( visibility( "default" ) ) ) +#endif + +// Other platforms don't need to define anything +#ifndef EFSW_API +#define EFSW_API +#endif #endif /// Type for a watch id @@ -65,27 +65,24 @@ typedef long efsw_watchid; /// Type for watcher typedef void* efsw_watcher; -enum efsw_action -{ - EFSW_ADD = 1, /// Sent when a file is created or renamed - EFSW_DELETE = 2, /// Sent when a file is deleted or renamed - EFSW_MODIFIED = 3, /// Sent when a file is modified - EFSW_MOVED = 4 /// Sent when a file is moved +enum efsw_action { + EFSW_ADD = 1, /// Sent when a file is created or renamed + EFSW_DELETE = 2, /// Sent when a file is deleted or renamed + EFSW_MODIFIED = 3, /// Sent when a file is modified + EFSW_MOVED = 4 /// Sent when a file is moved }; -enum efsw_error -{ - EFSW_NOTFOUND = -1, - EFSW_REPEATED = -2, - EFSW_OUTOFSCOPE = -3, - EFSW_NOTREADABLE = -4, - EFSW_REMOTE = -5, - EFSW_WATCHER_FAILED = -6, - EFSW_UNSPECIFIED = -7 +enum efsw_error { + EFSW_NOTFOUND = -1, + EFSW_REPEATED = -2, + EFSW_OUTOFSCOPE = -3, + EFSW_NOTREADABLE = -4, + EFSW_REMOTE = -5, + EFSW_WATCHER_FAILED = -6, + EFSW_UNSPECIFIED = -7 }; -enum efsw_option -{ +enum efsw_option { /// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and /// file system events may be dropped. For that, using a different (bigger) buffer size /// can be defined here, but note that this does not work for network drives, @@ -95,19 +92,37 @@ enum efsw_option /// For Windows, per default all events are captured but we might only be interested /// in a subset; the value of the option should be set to a bitwise or'ed set of /// FILE_NOTIFY_CHANGE_* flags. - EFSW_OPT_WIN_NOTIFY_FILTER = 2 + EFSW_OPT_WIN_NOTIFY_FILTER = 2, + /// For macOS (FSEvents backend), per default all modified event types are capture but we might + // only be interested in a subset; the value of the option should be set to a set of bitwise + // from: + // kFSEventStreamEventFlagItemFinderInfoMod + // kFSEventStreamEventFlagItemModified + // kFSEventStreamEventFlagItemInodeMetaMod + // Default configuration will set the 3 flags + EFSW_OPT_MAC_MODIFIED_FILTER = 3, + /// macOS sometimes informs incorrect or old file states that may confuse the consumer + /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing + /// 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. -typedef void (*efsw_pfn_fileaction_callback) ( - efsw_watcher watcher, - efsw_watchid watchid, - const char* dir, - const char* filename, - enum efsw_action action, - const char* old_filename, - void* param -); +typedef void ( *efsw_pfn_fileaction_callback )( efsw_watcher watcher, efsw_watchid watchid, + const char* dir, const char* filename, + enum efsw_action action, const char* old_filename, + void* param ); + +typedef void ( *efsw_pfn_handle_missed_fileactions )( efsw_watcher watcher, efsw_watchid watchid, + const char* dir ); typedef struct { enum efsw_option option; @@ -118,10 +133,10 @@ typedef struct { * Creates a new file-watcher * @param generic_mode Force the use of the Generic file watcher */ -efsw_watcher EFSW_API efsw_create(int generic_mode); +efsw_watcher EFSW_API efsw_create( int generic_mode ); /// Release the file-watcher and unwatch any directories -void EFSW_API efsw_release(efsw_watcher watcher); +void EFSW_API efsw_release( efsw_watcher watcher ); /// Retrieve last error occured by file-watcher EFSW_API const char* efsw_getlasterror(); @@ -131,47 +146,49 @@ EFSW_API void efsw_clearlasterror(); /// Add a directory watch /// On error returns WatchID with Error type. -efsw_watchid EFSW_API efsw_addwatch(efsw_watcher watcher, const char* directory, - efsw_pfn_fileaction_callback callback_fn, int recursive, void* param); +efsw_watchid EFSW_API efsw_addwatch( efsw_watcher watcher, const char* directory, + efsw_pfn_fileaction_callback callback_fn, int recursive, + void* param ); /// Add a directory watch, specifying options /// @param options Pointer to an array of watcher options /// @param nr_options Number of options referenced by \p options -efsw_watchid EFSW_API efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory, - efsw_pfn_fileaction_callback callback_fn, int recursive, efsw_watcher_option *options, - int options_number, void* param); +efsw_watchid EFSW_API efsw_addwatch_withoptions( + efsw_watcher watcher, const char* directory, efsw_pfn_fileaction_callback callback_fn, + int recursive, efsw_watcher_option* options, int options_number, void* param, + efsw_pfn_handle_missed_fileactions callback_fn_missed_file_actions ); /// Remove a directory watch. This is a brute force search O(nlogn). -void EFSW_API efsw_removewatch(efsw_watcher watcher, const char* directory); +void EFSW_API efsw_removewatch( efsw_watcher watcher, const char* directory ); /// Remove a directory watch. This is a map lookup O(logn). -void EFSW_API efsw_removewatch_byid(efsw_watcher watcher, efsw_watchid watchid); +void EFSW_API efsw_removewatch_byid( efsw_watcher watcher, efsw_watchid watchid ); /// Starts watching ( in other thread ) -void EFSW_API efsw_watch(efsw_watcher watcher); +void EFSW_API efsw_watch( efsw_watcher watcher ); /** * Allow recursive watchers to follow symbolic links to other directories * followSymlinks is disabled by default */ -void EFSW_API efsw_follow_symlinks(efsw_watcher watcher, int enable); +void EFSW_API efsw_follow_symlinks( efsw_watcher watcher, int enable ); /** @return If can follow symbolic links to directorioes */ -int EFSW_API efsw_follow_symlinks_isenabled(efsw_watcher watcher); +int EFSW_API efsw_follow_symlinks_isenabled( efsw_watcher watcher ); /** * When enable this it will allow symlinks to watch recursively out of the pointed directory. * follorSymlinks must be enabled to this work. - * For example, added symlink to /home/folder, and the symlink points to /, this by default is not allowed, - * it's only allowed to symlink anything from /home/ and deeper. This is to avoid great levels of recursion. - * Enabling this could lead in infinite recursion, and crash the watcher ( it will try not to avoid this ). - * Buy enabling out of scope links, it will allow this behavior. + * For example, added symlink to /home/folder, and the symlink points to /, this by default is not + * allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid great + * levels of recursion. Enabling this could lead in infinite recursion, and crash the watcher ( it + * will try not to avoid this ). Buy enabling out of scope links, it will allow this behavior. * allowOutOfScopeLinks are disabled by default. */ -void EFSW_API efsw_allow_outofscopelinks(efsw_watcher watcher, int allow); +void EFSW_API efsw_allow_outofscopelinks( efsw_watcher watcher, int allow ); /// @return Returns if out of scope links are allowed -int EFSW_API efsw_outofscopelinks_isallowed(efsw_watcher watcher); +int EFSW_API efsw_outofscopelinks_isallowed( efsw_watcher watcher ); #ifdef __cplusplus } diff --git a/dep/efsw/include/efsw/efsw.hpp b/dep/efsw/include/efsw/efsw.hpp index cb78ef3797f..60d00296913 100644 --- a/dep/efsw/include/efsw/efsw.hpp +++ b/dep/efsw/include/efsw/efsw.hpp @@ -1,7 +1,7 @@ /** @author Martín Lucas Golini - Copyright (c) 2013 Martín Lucas Golini + Copyright (c) 2024 Martín Lucas Golini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,6 @@ #ifndef ESFW_HPP #define ESFW_HPP -#include <vector> #include <string> #include <vector> @@ -135,7 +134,27 @@ enum Option { /// For Windows, per default all events are captured but we might only be interested /// in a subset; the value of the option should be set to a bitwise or'ed set of /// FILE_NOTIFY_CHANGE_* flags. - WinNotifyFilter = 2 + WinNotifyFilter = 2, + /// For macOS (FSEvents backend), per default all modified event types are capture but we might + /// only be interested in a subset; the value of the option should be set to a set of bitwise + /// from: + /// kFSEventStreamEventFlagItemFinderInfoMod + /// kFSEventStreamEventFlagItemModified + /// kFSEventStreamEventFlagItemInodeMetaMod + /// Default configuration will set the 3 flags + MacModifiedFilter = 3, + /// macOS sometimes informs incorrect or old file states that may confuse the consumer + /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing + /// 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; @@ -168,8 +187,8 @@ class EFSW_API FileWatcher { /// @param recursive Set this to true to include subdirectories /// @param options Allows customization of a watcher /// @return Returns the watch id for the directory or, on error, a WatchID with Error type. - WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, - const std::vector<WatcherOption> &options ); + WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, + const std::vector<WatcherOption>& options ); /// Remove a directory watch. This is a brute force search O(nlogn). void removeWatch( const std::string& directory ); @@ -226,13 +245,19 @@ class FileWatchListener { virtual void handleFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename = "" ) = 0; + + /// Handles that have missed file actions + /// @param watchid The watch id for the directory + /// @param dir The directory + virtual void handleMissedFileActions( WatchID /*watchid*/, + const std::string& /*dir*/ ) {} }; /// Optional, typically platform specific parameter for customization of a watcher. /// @class WatcherOption class WatcherOption { public: - WatcherOption(Option option, int value) : mOption(option), mValue(value) {}; + WatcherOption( Option option, int value ) : mOption( option ), mValue( value ){}; Option mOption; int mValue; }; diff --git a/dep/efsw/src/efsw/Debug.hpp b/dep/efsw/src/efsw/Debug.hpp index 78d35573b62..fefaec4d7e8 100644 --- a/dep/efsw/src/efsw/Debug.hpp +++ b/dep/efsw/src/efsw/Debug.hpp @@ -49,8 +49,10 @@ void efPRINTC( unsigned int cond, const char* format, ... ); #define efDEBUGC( cond, format, args... ) \ {} #else -#define efDEBUG -#define efDEBUGC +#define efDEBUG( ... ) \ + {} +#define efDEBUGC( ... ) \ + {} #endif #endif diff --git a/dep/efsw/src/efsw/FileSystem.cpp b/dep/efsw/src/efsw/FileSystem.cpp index b6d2d63ddce..1ed346ca17f 100644 --- a/dep/efsw/src/efsw/FileSystem.cpp +++ b/dep/efsw/src/efsw/FileSystem.cpp @@ -1,11 +1,19 @@ #include <cstring> #include <efsw/FileSystem.hpp> #include <efsw/platform/platformimpl.hpp> +#include <climits> #if EFSW_OS == EFSW_OS_MACOSX #include <CoreFoundation/CoreFoundation.h> #endif +#if EFSW_OS == EFSW_OS_WIN +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif + namespace efsw { bool FileSystem::isDirectory( const std::string& path ) { @@ -133,4 +141,21 @@ std::string FileSystem::getCurrentWorkingDirectory() { return Platform::FileSystem::getCurrentWorkingDirectory(); } +std::string FileSystem::getRealPath( const std::string& path ) { + std::string realPath; +#if defined( EFSW_PLATFORM_POSIX ) + char dir[PATH_MAX]; + realpath( path.c_str(), &dir[0] ); + realPath = std::string( dir ); +#elif EFSW_OS == EFSW_OS_WIN + wchar_t dir[_MAX_PATH + 1]; + GetFullPathNameW( String::fromUtf8( path ).toWideString().c_str(), _MAX_PATH, &dir[0], + nullptr ); + realPath = String( dir ).toUtf8(); +#else +#warning FileSystem::getRealPath() not implemented on this platform. +#endif + return realPath; +} + } // namespace efsw diff --git a/dep/efsw/src/efsw/FileSystem.hpp b/dep/efsw/src/efsw/FileSystem.hpp index 6c24386c585..1d66ece4972 100644 --- a/dep/efsw/src/efsw/FileSystem.hpp +++ b/dep/efsw/src/efsw/FileSystem.hpp @@ -3,7 +3,6 @@ #include <efsw/FileInfo.hpp> #include <efsw/base.hpp> -#include <map> namespace efsw { @@ -34,6 +33,9 @@ class FileSystem { static bool changeWorkingDirectory( const std::string& path ); static std::string getCurrentWorkingDirectory(); + + static std::string getRealPath( const std::string& path ); + }; } // namespace efsw diff --git a/dep/efsw/src/efsw/FileWatcherCWrapper.cpp b/dep/efsw/src/efsw/FileWatcherCWrapper.cpp index 860d7d50c8b..258f8e04473 100644 --- a/dep/efsw/src/efsw/FileWatcherCWrapper.cpp +++ b/dep/efsw/src/efsw/FileWatcherCWrapper.cpp @@ -10,10 +10,12 @@ class Watcher_CAPI : public efsw::FileWatchListener { efsw_watcher mWatcher; efsw_pfn_fileaction_callback mFn; void* mParam; + efsw_pfn_handle_missed_fileactions mFnMissedFa; public: - Watcher_CAPI( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param ) : - mWatcher( watcher ), mFn( fn ), mParam( param ) {} + Watcher_CAPI( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param, + efsw_pfn_handle_missed_fileactions fnfa ) : + mWatcher( watcher ), mFn( fn ), mParam( param ), mFnMissedFa( fnfa ) {} void handleFileAction( efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, @@ -21,6 +23,12 @@ class Watcher_CAPI : public efsw::FileWatchListener { mFn( mWatcher, watchid, dir.c_str(), filename.c_str(), (enum efsw_action)action, oldFilename.c_str(), mParam ); } + + void handleMissedFileActions( efsw::WatchID watchid, const std::string& dir ) { + if ( mFnMissedFa ) { + mFnMissedFa( mWatcher, watchid, dir.c_str() ); + } + } }; /************************************************************************************************* @@ -28,12 +36,12 @@ class Watcher_CAPI : public efsw::FileWatchListener { */ static std::vector<Watcher_CAPI*> g_callbacks; -Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn ) { +Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param ) { for ( std::vector<Watcher_CAPI*>::iterator i = g_callbacks.begin(); i != g_callbacks.end(); ++i ) { Watcher_CAPI* callback = *i; - if ( callback->mFn == fn && callback->mWatcher == watcher ) + if ( callback->mFn == fn && callback->mWatcher == watcher && callback->mParam == param ) return *i; } @@ -77,25 +85,27 @@ EFSW_API void efsw_clearlasterror() { efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory, efsw_pfn_fileaction_callback callback_fn, int recursive, void* param ) { - return efsw_addwatch_withoptions( watcher, directory, callback_fn, recursive, 0, 0, param ); + return efsw_addwatch_withoptions( watcher, directory, callback_fn, recursive, 0, 0, param, + nullptr ); } -efsw_watchid efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory, - efsw_pfn_fileaction_callback callback_fn, int recursive, - efsw_watcher_option *options, int options_number, - void* param) { - Watcher_CAPI* callback = find_callback( watcher, callback_fn ); +efsw_watchid +efsw_addwatch_withoptions( efsw_watcher watcher, const char* directory, + efsw_pfn_fileaction_callback callback_fn, int recursive, + efsw_watcher_option* options, int options_number, void* param, + efsw_pfn_handle_missed_fileactions callback_fn_missed_file_actions ) { + Watcher_CAPI* callback = find_callback( watcher, callback_fn, param ); if ( callback == NULL ) { - callback = new Watcher_CAPI( watcher, callback_fn, param ); + callback = new Watcher_CAPI( watcher, callback_fn, param, callback_fn_missed_file_actions ); g_callbacks.push_back( callback ); } std::vector<efsw::WatcherOption> watcher_options{}; for ( int i = 0; i < options_number; i++ ) { efsw_watcher_option* option = &options[i]; - watcher_options.emplace_back( efsw::WatcherOption{ - static_cast<efsw::Option>(option->option), option->value } ); + watcher_options.emplace_back( + efsw::WatcherOption{ static_cast<efsw::Option>( option->option ), option->value } ); } return ( (efsw::FileWatcher*)watcher ) diff --git a/dep/efsw/src/efsw/FileWatcherFSEvents.cpp b/dep/efsw/src/efsw/FileWatcherFSEvents.cpp index 0fa745242cf..70ec2b18233 100644 --- a/dep/efsw/src/efsw/FileWatcherFSEvents.cpp +++ b/dep/efsw/src/efsw/FileWatcherFSEvents.cpp @@ -41,7 +41,33 @@ bool FileWatcherFSEvents::isGranular() { return getOSXReleaseNumber() >= 11; } -void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef streamRef, void* userData, +static std::string convertCFStringToStdString( CFStringRef cfString ) { + // Try to get the C string pointer directly + const char* cStr = CFStringGetCStringPtr( cfString, kCFStringEncodingUTF8 ); + + if ( cStr ) { + // If the pointer is valid, directly return a std::string from it + return std::string( cStr ); + } else { + // If not, manually convert it + CFIndex length = CFStringGetLength( cfString ); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) + + 1; // +1 for null terminator + + char* buffer = new char[maxSize]; + + if ( CFStringGetCString( cfString, buffer, maxSize, kCFStringEncodingUTF8 ) ) { + std::string result( buffer ); + delete[] buffer; + return result; + } else { + delete[] buffer; + return ""; + } + } +} + +void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[] ) { @@ -51,8 +77,24 @@ void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef streamRef, void events.reserve( numEvents ); for ( size_t i = 0; i < numEvents; i++ ) { - events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ), (long)eventFlags[i], - (Uint64)eventIds[i] ) ); + if ( isGranular() ) { + CFDictionaryRef pathInfoDict = + static_cast<CFDictionaryRef>( CFArrayGetValueAtIndex( (CFArrayRef)eventPaths, i ) ); + CFStringRef path = static_cast<CFStringRef>( + CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedDataPathKey ) ); + CFNumberRef cfInode = static_cast<CFNumberRef>( + CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedFileIDKey ) ); + + if ( cfInode ) { + unsigned long inode = 0; + CFNumberGetValue( cfInode, kCFNumberLongType, &inode ); + events.push_back( FSEvent( convertCFStringToStdString( path ), (long)eventFlags[i], + (Uint64)eventIds[i], inode ) ); + } + } else { + events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ), + (long)eventFlags[i], (Uint64)eventIds[i] ) ); + } } watcher->handleActions( events ); @@ -84,8 +126,8 @@ FileWatcherFSEvents::~FileWatcherFSEvents() { } WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive, const std::vector<WatcherOption> &options ) { - std::string dir( directory ); + bool recursive, const std::vector<WatcherOption>& options ) { + std::string dir( FileSystem::getRealPath( directory ) ); FileInfo fi( dir ); @@ -125,6 +167,9 @@ WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchLi pWatch->Directory = dir; pWatch->Recursive = recursive; pWatch->FWatcher = this; + pWatch->ModifiedFlags = + getOptionValue( options, Option::MacModifiedFilter, efswFSEventsModified ); + pWatch->SanitizeEvents = getOptionValue( options, Option::MacSanitizeEvents, 0 ) != 0; pWatch->init(); @@ -169,8 +214,8 @@ void FileWatcherFSEvents::removeWatch( WatchID watchid ) { void FileWatcherFSEvents::watch() {} -void FileWatcherFSEvents::handleAction( Watcher* watch, const std::string& filename, - unsigned long action, std::string oldFilename ) { +void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, + unsigned long /*action*/, std::string /*oldFilename*/ ) { /// Not used } diff --git a/dep/efsw/src/efsw/FileWatcherFSEvents.hpp b/dep/efsw/src/efsw/FileWatcherFSEvents.hpp index 5ad182ed43f..daa538cc6fd 100644 --- a/dep/efsw/src/efsw/FileWatcherFSEvents.hpp +++ b/dep/efsw/src/efsw/FileWatcherFSEvents.hpp @@ -16,27 +16,6 @@ namespace efsw { -/* OSX < 10.7 has no file events */ -/* So i declare the events constants */ -enum FSEventEvents { - efswFSEventStreamCreateFlagNoDefer = 0x00000002, - efswFSEventStreamCreateFlagFileEvents = 0x00000010, - efswFSEventStreamEventFlagItemCreated = 0x00000100, - efswFSEventStreamEventFlagItemRemoved = 0x00000200, - efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400, - efswFSEventStreamEventFlagItemRenamed = 0x00000800, - efswFSEventStreamEventFlagItemModified = 0x00001000, - efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000, - efswFSEventStreamEventFlagItemChangeOwner = 0x00004000, - efswFSEventStreamEventFlagItemXattrMod = 0x00008000, - efswFSEventStreamEventFlagItemIsFile = 0x00010000, - efswFSEventStreamEventFlagItemIsDir = 0x00020000, - efswFSEventStreamEventFlagItemIsSymlink = 0x00040000, - efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod | - efswFSEventStreamEventFlagItemModified | - efswFSEventStreamEventFlagItemInodeMetaMod -}; - /// Implementation for Win32 based on ReadDirectoryChangesW. /// @class FileWatcherFSEvents class FileWatcherFSEvents : public FileWatcherImpl { diff --git a/dep/efsw/src/efsw/FileWatcherGeneric.cpp b/dep/efsw/src/efsw/FileWatcherGeneric.cpp index 3f3c52e47fb..e0d393056a0 100644 --- a/dep/efsw/src/efsw/FileWatcherGeneric.cpp +++ b/dep/efsw/src/efsw/FileWatcherGeneric.cpp @@ -101,7 +101,7 @@ void FileWatcherGeneric::removeWatch( WatchID watchid ) { void FileWatcherGeneric::watch() { if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherGeneric::run, this ); + mThread = new Thread([this]{run();}); mThread->launch(); } } diff --git a/dep/efsw/src/efsw/FileWatcherInotify.cpp b/dep/efsw/src/efsw/FileWatcherInotify.cpp index 29be12b6262..cb751ba88ee 100644 --- a/dep/efsw/src/efsw/FileWatcherInotify.cpp +++ b/dep/efsw/src/efsw/FileWatcherInotify.cpp @@ -7,6 +7,7 @@ #include <fcntl.h> #include <stdio.h> #include <string.h> +#include <sys/select.h> #include <sys/stat.h> #include <unistd.h> @@ -69,15 +70,17 @@ FileWatcherInotify::~FileWatcherInotify() { } WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, - bool recursive, const std::vector<WatcherOption>& ) { + bool recursive, const std::vector<WatcherOption>& 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 ); @@ -137,6 +140,7 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis pWatch->Directory = dir; pWatch->Recursive = recursive; pWatch->Parent = parent; + pWatch->syntheticEvents = syntheticEvents; { Lock lock( mWatchesLock ); @@ -151,6 +155,18 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis if ( pWatch->Recursive ) { std::map<std::string, FileInfo> files = FileSystem::filesInfoFromPath( pWatch->Directory ); + + if ( fromInternalEvent && parent != NULL && syntheticEvents ) { + for ( const auto& file : files ) { + if ( file.second.isRegularFile() || file.second.isDirectory() || + file.second.isLink() ) { + pWatch->Listener->handleFileAction( + pWatch->ID, pWatch->Directory, + FileSystem::fileNameFromPath( file.second.Filepath ), Actions::Add ); + } + } + } + std::map<std::string, FileInfo>::iterator it = files.begin(); for ( ; it != files.end(); ++it ) { @@ -160,7 +176,8 @@ 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, + fromInternalEvent ); } } } @@ -244,7 +261,7 @@ void FileWatcherInotify::removeWatch( WatchID watchid ) { void FileWatcherInotify::watch() { if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherInotify::run, this ); + mThread = new Thread( [this] { run(); } ); mThread->launch(); } } @@ -267,10 +284,10 @@ Watcher* FileWatcherInotify::watcherContainsDirectory( std::string dir ) { void FileWatcherInotify::run() { char* buff = new char[BUFF_SIZE]; memset( buff, 0, BUFF_SIZE ); - WatchMap::iterator wit; + WatcherInotify* curWatcher = NULL; WatcherInotify* currentMoveFrom = NULL; - u_int32_t currentMoveCookie = -1; + uint32_t currentMoveCookie = -1; bool lastWasMovedFrom = false; std::string prevOldFileName; @@ -294,16 +311,21 @@ void FileWatcherInotify::run() { struct inotify_event* pevent = (struct inotify_event*)&buff[i]; { + curWatcher = NULL; + { Lock lock( mWatchesLock ); - wit = mWatches.find( pevent->wd ); + auto wit = mWatches.find( pevent->wd ); + + if ( wit != mWatches.end() ) + curWatcher = wit->second; } - if ( wit != mWatches.end() ) { - handleAction( wit->second, (char*)pevent->name, pevent->mask ); + if ( curWatcher ) { + handleAction( curWatcher, (char*)pevent->name, pevent->mask ); - if ( ( pevent->mask & IN_MOVED_TO ) && wit->second == currentMoveFrom && + if ( ( pevent->mask & IN_MOVED_TO ) && curWatcher == currentMoveFrom && pevent->cookie == currentMoveCookie ) { /// make pair success currentMoveFrom = NULL; @@ -316,14 +338,30 @@ void FileWatcherInotify::run() { std::make_pair( currentMoveFrom, prevOldFileName ) ); } - currentMoveFrom = wit->second; + currentMoveFrom = curWatcher; currentMoveCookie = pevent->cookie; } else { /// Keep track of the IN_MOVED_FROM events to know /// if the IN_MOVED_TO event is also fired if ( currentMoveFrom ) { - mMovedOutsideWatches.push_back( - std::make_pair( currentMoveFrom, prevOldFileName ) ); + if ( std::find_if( mMovedOutsideWatches.begin(), + mMovedOutsideWatches.end(), + [currentMoveFrom]( + const std::pair<WatcherInotify*, + std::string>& moved ) { + return moved.first == currentMoveFrom; + } ) == mMovedOutsideWatches.end() ) { + mMovedOutsideWatches.push_back( + std::make_pair( currentMoveFrom, prevOldFileName ) ); + } else { + efDEBUG( "Info: Tried to add watch to the moved outside " + "watches but it was already there, Watch ID: %d - " + "Address: %p - Path: \"%s\" - prevOldFileName: " + "\"%s\"\n", + pevent->wd, currentMoveFrom, + currentMoveFrom->Directory.c_str(), + prevOldFileName.c_str() ); + } } currentMoveFrom = NULL; @@ -343,8 +381,18 @@ void FileWatcherInotify::run() { // Here means no event received // If last event is IN_MOVED_FROM, we assume no IN_MOVED_TO if ( currentMoveFrom ) { - mMovedOutsideWatches.push_back( - std::make_pair( currentMoveFrom, currentMoveFrom->OldFileName ) ); + if ( std::find_if( + mMovedOutsideWatches.begin(), mMovedOutsideWatches.end(), + [currentMoveFrom]( const std::pair<WatcherInotify*, std::string>& moved ) { + return moved.first == currentMoveFrom; + } ) == mMovedOutsideWatches.end() ) { + mMovedOutsideWatches.push_back( + std::make_pair( currentMoveFrom, currentMoveFrom->OldFileName ) ); + } else { + efDEBUG( "Warning: Tried to add watch to the moved outside " + "watches but it was already there, Watch Address: %p\n", + currentMoveFrom ); + } } currentMoveFrom = NULL; @@ -378,8 +426,8 @@ void FileWatcherInotify::run() { continue; } - Watcher* watch = ( *it ).first; - const std::string& oldFileName = ( *it ).second; + Watcher* watch = it->first; + const std::string& oldFileName = it->second; /// Check if the file move was a folder already being watched std::vector<Watcher*> eraseWatches; @@ -387,8 +435,8 @@ void FileWatcherInotify::run() { { Lock lock( mWatchesLock ); - for ( ; wit != mWatches.end(); ++wit ) { - Watcher* oldWatch = wit->second; + for ( auto wit : mWatches ) { + Watcher* oldWatch = wit.second; if ( oldWatch != watch && -1 != String::strStartsWith( watch->Directory + oldFileName + "/", @@ -448,8 +496,9 @@ void FileWatcherInotify::checkForNewWatcher( Watcher* watch, std::string fpath ) } if ( !found ) { - addWatch( fpath, watch->Listener, watch->Recursive, - static_cast<WatcherInotify*>( watch ) ); + WatcherInotify* iWatch = static_cast<WatcherInotify*>( watch ); + addWatch( fpath, watch->Listener, watch->Recursive, iWatch->syntheticEvents, + static_cast<WatcherInotify*>( watch ), true ); } } } @@ -464,7 +513,9 @@ void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filena std::string fpath( watch->Directory + filename ); - if ( ( IN_CLOSE_WRITE & action ) || ( IN_MODIFY & action ) ) { + if ( IN_Q_OVERFLOW & action ) { + watch->Listener->handleMissedFileActions( watch->ID, watch->Directory ); + } else if ( ( IN_CLOSE_WRITE & action ) || ( IN_MODIFY & action ) ) { watch->Listener->handleFileAction( watch->ID, watch->Directory, filename, Actions::Modified ); } else if ( IN_MOVED_TO & action ) { diff --git a/dep/efsw/src/efsw/FileWatcherInotify.hpp b/dep/efsw/src/efsw/FileWatcherInotify.hpp index 84174a0676a..26d2c0b28e3 100644 --- a/dep/efsw/src/efsw/FileWatcherInotify.hpp +++ b/dep/efsw/src/efsw/FileWatcherInotify.hpp @@ -65,7 +65,8 @@ class FileWatcherInotify : public FileWatcherImpl { std::vector<std::pair<WatcherInotify*, std::string>> 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/dep/efsw/src/efsw/FileWatcherKqueue.cpp b/dep/efsw/src/efsw/FileWatcherKqueue.cpp index 32ef3dc82c8..41b4357e6ae 100644 --- a/dep/efsw/src/efsw/FileWatcherKqueue.cpp +++ b/dep/efsw/src/efsw/FileWatcherKqueue.cpp @@ -162,7 +162,7 @@ bool FileWatcherKqueue::isAddingWatcher() const { void FileWatcherKqueue::watch() { if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherKqueue::run, this ); + mThread = new Thread([this]{run();}); mThread->launch(); } } diff --git a/dep/efsw/src/efsw/FileWatcherWin32.cpp b/dep/efsw/src/efsw/FileWatcherWin32.cpp index 37f43cc27da..1dd96aa7945 100644 --- a/dep/efsw/src/efsw/FileWatcherWin32.cpp +++ b/dep/efsw/src/efsw/FileWatcherWin32.cpp @@ -26,7 +26,8 @@ FileWatcherWin32::~FileWatcherWin32() { removeAllWatches(); - CloseHandle( mIOCP ); + if ( mIOCP ) + CloseHandle( mIOCP ); } WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher, @@ -112,7 +113,7 @@ void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { void FileWatcherWin32::watch() { if ( NULL == mThread ) { - mThread = new Thread( &FileWatcherWin32::run, this ); + mThread = new Thread([this]{run();}); mThread->launch(); } } @@ -143,7 +144,8 @@ void FileWatcherWin32::run() { break; } else { Lock lock( mWatchesLock ); - WatchCallback( numOfBytes, ov ); + if (mWatches.find( (WatcherStructWin32*)ov ) != mWatches.end()) + WatchCallback( numOfBytes, ov ); } } } else { diff --git a/dep/efsw/src/efsw/FileWatcherWin32.hpp b/dep/efsw/src/efsw/FileWatcherWin32.hpp index de3f9538dcd..3f6f419bdbd 100644 --- a/dep/efsw/src/efsw/FileWatcherWin32.hpp +++ b/dep/efsw/src/efsw/FileWatcherWin32.hpp @@ -7,7 +7,7 @@ #include <efsw/WatcherWin32.hpp> #include <map> -#include <set> +#include <unordered_set> #include <vector> namespace efsw { @@ -17,7 +17,7 @@ namespace efsw { class FileWatcherWin32 : public FileWatcherImpl { public: /// type for a map from WatchID to WatcherWin32 pointer - typedef std::set<WatcherStructWin32*> Watches; + typedef std::unordered_set<WatcherStructWin32*> Watches; FileWatcherWin32( FileWatcher* parent ); diff --git a/dep/efsw/src/efsw/Lock.hpp b/dep/efsw/src/efsw/Lock.hpp index e8c522abf0d..714f3aa64b2 100644 --- a/dep/efsw/src/efsw/Lock.hpp +++ b/dep/efsw/src/efsw/Lock.hpp @@ -1,21 +1,11 @@ #ifndef EFSW_LOCK_HPP #define EFSW_LOCK_HPP +#include <mutex> #include <efsw/Mutex.hpp> namespace efsw { - -/** Simple mutex class */ -class Lock { - public: - explicit Lock( Mutex& mutex ) : mMutex( mutex ) { mMutex.lock(); } - - ~Lock() { mMutex.unlock(); } - - private: - Mutex& mMutex; -}; - + using Lock = std::unique_lock<Mutex>; } // namespace efsw #endif diff --git a/dep/efsw/src/efsw/Mutex.cpp b/dep/efsw/src/efsw/Mutex.cpp deleted file mode 100644 index c961db18124..00000000000 --- a/dep/efsw/src/efsw/Mutex.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include <efsw/Mutex.hpp> -#include <efsw/platform/platformimpl.hpp> - -namespace efsw { - -Mutex::Mutex() : mMutexImpl( new Platform::MutexImpl() ) {} - -Mutex::~Mutex() { - efSAFE_DELETE( mMutexImpl ); -} - -void Mutex::lock() { - mMutexImpl->lock(); -} - -void Mutex::unlock() { - mMutexImpl->unlock(); -} - -} // namespace efsw diff --git a/dep/efsw/src/efsw/Mutex.hpp b/dep/efsw/src/efsw/Mutex.hpp index d98ad17c237..c1fca753863 100644 --- a/dep/efsw/src/efsw/Mutex.hpp +++ b/dep/efsw/src/efsw/Mutex.hpp @@ -1,31 +1,10 @@ #ifndef EFSW_MUTEX_HPP #define EFSW_MUTEX_HPP -#include <efsw/base.hpp> +#include <mutex> namespace efsw { - -namespace Platform { -class MutexImpl; -} - -/** Simple mutex class */ -class Mutex { - public: - Mutex(); - - ~Mutex(); - - /** Lock the mutex */ - void lock(); - - /** Unlock the mutex */ - void unlock(); - - private: - Platform::MutexImpl* mMutexImpl; -}; - + using Mutex = std::recursive_mutex; } // namespace efsw #endif diff --git a/dep/efsw/src/efsw/String.hpp b/dep/efsw/src/efsw/String.hpp index 65bce3328c2..b42b945354b 100644 --- a/dep/efsw/src/efsw/String.hpp +++ b/dep/efsw/src/efsw/String.hpp @@ -11,7 +11,6 @@ #include <cstdlib> #include <cstring> #include <efsw/base.hpp> -#include <fstream> #include <iostream> #include <locale> #include <sstream> @@ -24,7 +23,7 @@ namespace efsw { * **/ class String { public: - typedef Uint32 StringBaseType; + typedef char32_t StringBaseType; typedef std::basic_string<StringBaseType> StringType; typedef StringType::iterator Iterator; //! Iterator type typedef StringType::const_iterator ConstIterator; //! Constant iterator type diff --git a/dep/efsw/src/efsw/Thread.cpp b/dep/efsw/src/efsw/Thread.cpp deleted file mode 100644 index cfa88b482d3..00000000000 --- a/dep/efsw/src/efsw/Thread.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include <efsw/Thread.hpp> -#include <efsw/platform/platformimpl.hpp> - -namespace efsw { - -Thread::Thread() : mThreadImpl( NULL ), mEntryPoint( NULL ) {} - -Thread::~Thread() { - wait(); - - efSAFE_DELETE( mEntryPoint ); -} - -void Thread::launch() { - wait(); - - mThreadImpl = new Platform::ThreadImpl( this ); -} - -void Thread::wait() { - if ( mThreadImpl ) { - mThreadImpl->wait(); - - efSAFE_DELETE( mThreadImpl ); - } -} - -void Thread::terminate() { - if ( mThreadImpl ) { - mThreadImpl->terminate(); - - efSAFE_DELETE( mThreadImpl ); - } -} - -void Thread::run() { - if ( mEntryPoint ) - mEntryPoint->run(); -} - -} // namespace efsw diff --git a/dep/efsw/src/efsw/Thread.hpp b/dep/efsw/src/efsw/Thread.hpp index b60373c2075..5a5d470e10b 100644 --- a/dep/efsw/src/efsw/Thread.hpp +++ b/dep/efsw/src/efsw/Thread.hpp @@ -2,99 +2,48 @@ #define EFSW_THREAD_HPP #include <efsw/base.hpp> +#include <functional> +#include <memory> +#include <thread> namespace efsw { -namespace Platform { -class ThreadImpl; -} -namespace Private { -struct ThreadFunc; -} - /** @brief Thread manager class */ class Thread { public: - typedef void ( *FuncType )( void* ); - - template <typename F> Thread( F function ); - - template <typename F, typename A> Thread( F function, A argument ); - template <typename C> Thread( void ( C::*function )(), C* object ); + Thread(std::function<void()> fun) + : mFun{std::move(fun)} + { + } - virtual ~Thread(); + ~Thread() + { + wait(); + } /** Launch the thread */ - virtual void launch(); + void launch() + { + if (!mThread) + mThread.reset(new std::thread{std::move(mFun)}); + } /** Wait the thread until end */ - void wait(); - - /** Terminate the thread */ - void terminate(); - - protected: - Thread(); - - private: - friend class Platform::ThreadImpl; - - /** The virtual function to run in the thread */ - virtual void run(); - - Platform::ThreadImpl* mThreadImpl; ///< OS-specific implementation of the thread - Private::ThreadFunc* mEntryPoint; ///< Abstraction of the function to run -}; - -//! NOTE: Taken from SFML2 threads -namespace Private { - -// Base class for abstract thread functions -struct ThreadFunc { - virtual ~ThreadFunc() {} - virtual void run() = 0; + void wait() + { + if (mThread) + { + mThread->join(); + mThread.reset(); + } + } +private: + + std::unique_ptr<std::thread> mThread; + std::function<void()> mFun; }; -// Specialization using a functor (including free functions) with no argument -template <typename T> struct ThreadFunctor : ThreadFunc { - ThreadFunctor( T functor ) : m_functor( functor ) {} - virtual void run() { m_functor(); } - T m_functor; -}; - -// Specialization using a functor (including free functions) with one argument -template <typename F, typename A> struct ThreadFunctorWithArg : ThreadFunc { - ThreadFunctorWithArg( F function, A arg ) : m_function( function ), m_arg( arg ) {} - virtual void run() { m_function( m_arg ); } - F m_function; - A m_arg; -}; - -// Specialization using a member function -template <typename C> struct ThreadMemberFunc : ThreadFunc { - ThreadMemberFunc( void ( C::*function )(), C* object ) : - m_function( function ), m_object( object ) {} - virtual void run() { ( m_object->*m_function )(); } - void ( C::*m_function )(); - C* m_object; -}; - -} // namespace Private - -template <typename F> -Thread::Thread( F functor ) : - mThreadImpl( NULL ), mEntryPoint( new Private::ThreadFunctor<F>( functor ) ) {} - -template <typename F, typename A> -Thread::Thread( F function, A argument ) : - mThreadImpl( NULL ), - mEntryPoint( new Private::ThreadFunctorWithArg<F efCOMMA A>( function, argument ) ) {} - -template <typename C> -Thread::Thread( void ( C::*function )(), C* object ) : - mThreadImpl( NULL ), mEntryPoint( new Private::ThreadMemberFunc<C>( function, object ) ) {} - } // namespace efsw #endif diff --git a/dep/efsw/src/efsw/WatcherFSEvents.cpp b/dep/efsw/src/efsw/WatcherFSEvents.cpp index 3621df746b5..bc982bf7e1d 100644 --- a/dep/efsw/src/efsw/WatcherFSEvents.cpp +++ b/dep/efsw/src/efsw/WatcherFSEvents.cpp @@ -10,13 +10,6 @@ namespace efsw { WatcherFSEvents::WatcherFSEvents() : Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {} -WatcherFSEvents::WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener, - bool recursive, WatcherFSEvents* parent ) : - Watcher( id, directory, listener, recursive ), - FWatcher( NULL ), - FSStream( NULL ), - WatcherGen( NULL ) {} - WatcherFSEvents::~WatcherFSEvents() { if ( NULL != FSStream ) { FSEventStreamStop( FSStream ); @@ -35,7 +28,9 @@ void WatcherFSEvents::init() { Uint32 streamFlags = kFSEventStreamCreateFlagNone; if ( FileWatcherFSEvents::isGranular() ) { - streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer; + streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer | + efswFSEventStreamCreateFlagUseExtendedData | + efswFSEventStreamCreateFlagUseCFTypes; } else { WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive ); } @@ -48,13 +43,13 @@ void WatcherFSEvents::init() { ctx.release = NULL; ctx.copyDescription = NULL; - dispatch_queue_t queue = dispatch_queue_create(NULL, NULL); + dispatch_queue_t queue = dispatch_queue_create( NULL, NULL ); FSStream = FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx, CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0., streamFlags ); - FSEventStreamSetDispatchQueue(FSStream, queue); + FSEventStreamSetDispatchQueue( FSStream, queue ); FSEventStreamStart( FSStream ); @@ -66,27 +61,37 @@ void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename ) { Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ), - FileSystem::precomposeFileName( filename ), action, FileSystem::precomposeFileName( oldFilename ) ); + FileSystem::precomposeFileName( filename ), action, + FileSystem::precomposeFileName( oldFilename ) ); +} + +void WatcherFSEvents::sendMissedFileActions( WatchID watchid, + const std::string& dir) { + Listener->handleMissedFileActions( watchid, + FileSystem::precomposeFileName( dir ) ); } void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path, - std::string& dirPath, std::string& filePath ) { - if ( flags & efswFSEventStreamEventFlagItemCreated ) { - if ( FileInfo::exists( path ) ) { - sendFileAction( ID, dirPath, filePath, Actions::Add ); - } + std::string& dirPath, std::string& filePath, Uint64 inode ) { + if ( ( flags & efswFSEventStreamEventFlagItemCreated ) && FileInfo::exists( path ) && + ( !SanitizeEvents || FilesAdded.find( inode ) != FilesAdded.end() ) ) { + sendFileAction( ID, dirPath, filePath, Actions::Add ); + + if ( SanitizeEvents ) + FilesAdded.insert( inode ); } - if ( flags & efswFSEventsModified ) { + if ( flags & ModifiedFlags ) { sendFileAction( ID, dirPath, filePath, Actions::Modified ); } - if ( flags & efswFSEventStreamEventFlagItemRemoved ) { + if ( ( flags & efswFSEventStreamEventFlagItemRemoved ) && !FileInfo::exists( path ) ) { // Since i don't know the order, at least i try to keep the data consistent with the real // state - if ( !FileInfo::exists( path ) ) { - sendFileAction( ID, dirPath, filePath, Actions::Delete ); - } + sendFileAction( ID, dirPath, filePath, Actions::Delete ); + + if ( SanitizeEvents ) + FilesAdded.erase( inode ); } } @@ -98,7 +103,16 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) { if ( event.Flags & ( kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped | - kFSEventStreamEventFlagEventIdsWrapped | kFSEventStreamEventFlagHistoryDone | + kFSEventStreamEventFlagMustScanSubDirs) ) { + efDEBUG( "Rescan/Drop event for watch: %s - flags: 0x%x\n", Directory.c_str(), event.Flags ); + std::string dirPath = Directory; + FileSystem::dirRemoveSlashAtEnd( dirPath ); + sendMissedFileActions(ID, dirPath ); + continue; + } + + if ( event.Flags & + ( kFSEventStreamEventFlagEventIdsWrapped | kFSEventStreamEventFlagHistoryDone | kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount | kFSEventStreamEventFlagRootChanged ) ) { continue; @@ -128,19 +142,20 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) { // been added modified and erased, but i can't know if first was erased and then added // and modified, or added, then modified and then erased. I don't know what they were // thinking by doing this... - efDEBUG( "Event in: %s - flags: %ld\n", event.Path.c_str(), event.Flags ); + efDEBUG( "Event in: %s - flags: 0x%x\n", event.Path.c_str(), event.Flags ); if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) { if ( ( i + 1 < esize ) && ( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) && - ( events[i + 1].Id == event.Id + 1 ) ) { + ( events[i + 1].inode == event.inode ) ) { FSEvent& nEvent = events[i + 1]; std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) ); std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) ); if ( event.Path != nEvent.Path ) { if ( dirPath == newDir ) { - if ( !FileInfo::exists( event.Path ) ) { + if ( !FileInfo::exists( event.Path ) || + 0 == strcasecmp( event.Path.c_str(), nEvent.Path.c_str() ) ) { sendFileAction( ID, dirPath, newFilepath, Actions::Moved, filePath ); } else { @@ -151,12 +166,12 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) { sendFileAction( ID, dirPath, filePath, Actions::Delete ); sendFileAction( ID, newDir, newFilepath, Actions::Add ); - if ( nEvent.Flags & efswFSEventsModified ) { + if ( nEvent.Flags & ModifiedFlags ) { sendFileAction( ID, newDir, newFilepath, Actions::Modified ); } } } else { - handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath ); + handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath, event.inode ); } if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated | @@ -172,14 +187,14 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) { } else if ( FileInfo::exists( event.Path ) ) { sendFileAction( ID, dirPath, filePath, Actions::Add ); - if ( event.Flags & efswFSEventsModified ) { + if ( event.Flags & ModifiedFlags ) { sendFileAction( ID, dirPath, filePath, Actions::Modified ); } } else { sendFileAction( ID, dirPath, filePath, Actions::Delete ); } } else { - handleAddModDel( event.Flags, event.Path, dirPath, filePath ); + handleAddModDel( event.Flags, event.Path, dirPath, filePath, event.inode ); } } else { efDEBUG( "Directory: %s changed\n", event.Path.c_str() ); @@ -189,7 +204,7 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) { } void WatcherFSEvents::process() { - std::set<std::string>::iterator it = DirsChanged.begin(); + std::unordered_set<std::string>::iterator it = DirsChanged.begin(); for ( ; it != DirsChanged.end(); it++ ) { if ( !FileWatcherFSEvents::isGranular() ) { diff --git a/dep/efsw/src/efsw/WatcherFSEvents.hpp b/dep/efsw/src/efsw/WatcherFSEvents.hpp index a18c06d40b9..fe17ed4db17 100644 --- a/dep/efsw/src/efsw/WatcherFSEvents.hpp +++ b/dep/efsw/src/efsw/WatcherFSEvents.hpp @@ -9,29 +9,51 @@ #include <CoreServices/CoreServices.h> #include <efsw/FileInfo.hpp> #include <efsw/WatcherGeneric.hpp> -#include <set> +#include <unordered_set> #include <vector> namespace efsw { +/* OSX < 10.7 has no file events */ +/* So i declare the events constants */ +enum FSEventEvents { + efswFSEventStreamCreateFlagUseCFTypes = 0x00000001, + efswFSEventStreamCreateFlagNoDefer = 0x00000002, + efswFSEventStreamCreateFlagFileEvents = 0x00000010, + efswFSEventStreamCreateFlagUseExtendedData = 0x00000040, + efswFSEventStreamEventFlagItemCreated = 0x00000100, + efswFSEventStreamEventFlagItemRemoved = 0x00000200, + efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400, + efswFSEventStreamEventFlagItemRenamed = 0x00000800, + efswFSEventStreamEventFlagItemModified = 0x00001000, + efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000, + efswFSEventStreamEventFlagItemChangeOwner = 0x00004000, + efswFSEventStreamEventFlagItemXattrMod = 0x00008000, + efswFSEventStreamEventFlagItemIsFile = 0x00010000, + efswFSEventStreamEventFlagItemIsDir = 0x00020000, + efswFSEventStreamEventFlagItemIsSymlink = 0x00040000, + efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod | + efswFSEventStreamEventFlagItemModified | + efswFSEventStreamEventFlagItemInodeMetaMod +}; + class FileWatcherFSEvents; class FSEvent { public: - FSEvent( std::string path, long flags, Uint64 id ) : Path( path ), Flags( flags ), Id( id ) {} + FSEvent( std::string path, long flags, Uint64 id, Uint64 inode = 0 ) : + Path( path ), Flags( flags ), Id( id ), inode( inode ) {} std::string Path; - long Flags; - Uint64 Id; + long Flags{ 0 }; + Uint64 Id{ 0 }; + Uint64 inode{ 0 }; }; class WatcherFSEvents : public Watcher { public: WatcherFSEvents(); - WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener, bool recursive, - WatcherFSEvents* parent = NULL ); - ~WatcherFSEvents(); void init(); @@ -42,17 +64,22 @@ class WatcherFSEvents : public Watcher { Atomic<FileWatcherFSEvents*> FWatcher; FSEventStreamRef FSStream; + Uint64 ModifiedFlags{ efswFSEventsModified }; + bool SanitizeEvents{ false }; protected: void handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath, - std::string& filePath ); + std::string& filePath, Uint64 inode ); WatcherGeneric* WatcherGen; - std::set<std::string> DirsChanged; + std::unordered_set<std::string> DirsChanged; + std::unordered_set<Uint64> FilesAdded; void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, Action action, std::string oldFilename = "" ); + + void sendMissedFileActions( WatchID watchid, const std::string& dir); }; } // namespace efsw diff --git a/dep/efsw/src/efsw/WatcherInotify.hpp b/dep/efsw/src/efsw/WatcherInotify.hpp index d43935c5009..ec55ed0e73e 100644 --- a/dep/efsw/src/efsw/WatcherInotify.hpp +++ b/dep/efsw/src/efsw/WatcherInotify.hpp @@ -16,6 +16,7 @@ class WatcherInotify : public Watcher { WatchID InotifyID; FileInfo DirInfo; + bool syntheticEvents{ false }; }; } // namespace efsw diff --git a/dep/efsw/src/efsw/WatcherKqueue.cpp b/dep/efsw/src/efsw/WatcherKqueue.cpp index 397264162e5..424b9890e0d 100644 --- a/dep/efsw/src/efsw/WatcherKqueue.cpp +++ b/dep/efsw/src/efsw/WatcherKqueue.cpp @@ -354,7 +354,8 @@ void WatcherKqueue::watch() { bool needScan = false; // Then we get the the events of the current folder - while ( ( nev = kevent( mKqueue, &mChangeList[0], mChangeListCount + 1, &event, 1, + while ( !mChangeList.empty() && + ( nev = kevent( mKqueue, mChangeList.data(), mChangeListCount + 1, &event, 1, &mWatcher->mTimeOut ) ) != 0 ) { // An error ocurred? if ( nev == -1 ) { diff --git a/dep/efsw/src/efsw/WatcherWin32.cpp b/dep/efsw/src/efsw/WatcherWin32.cpp index ad206e35f71..d8d47aa7c5e 100644 --- a/dep/efsw/src/efsw/WatcherWin32.cpp +++ b/dep/efsw/src/efsw/WatcherWin32.cpp @@ -1,4 +1,5 @@ #include <efsw/Debug.hpp> +#include <efsw/FileSystem.hpp> #include <efsw/String.hpp> #include <efsw/WatcherWin32.hpp> @@ -8,30 +9,95 @@ namespace efsw { -/// Unpacks events and passes them to a user defined callback. -void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { +struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX { + DWORD NextEntryOffset; + DWORD Action; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastModificationTime; + LARGE_INTEGER LastChangeTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER AllocatedLength; + LARGE_INTEGER FileSize; + DWORD FileAttributes; + DWORD ReparsePointTag; + LARGE_INTEGER FileId; + LARGE_INTEGER ParentFileId; + DWORD FileNameLength; + WCHAR FileName[1]; +}; - if ( NULL == lpOverlapped ) { - return; +typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX; + +typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer, + DWORD nBufferLength, BOOL bWatchSubtree, + DWORD dwNotifyFilter, LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + DWORD ReadDirectoryNotifyInformationClass ); + +static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL; + +#define EFSW_ReadDirectoryNotifyExtendedInformation 2 + +static void initReadDirectoryChangesEx() { + static bool hasInit = false; + if ( !hasInit ) { + hasInit = true; + + HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" ); + if ( !hModule ) + return; + + pReadDirectoryChangesExW = + (EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" ); } +} +void WatchCallbackOld( WatcherWin32* pWatch ) { PFILE_NOTIFY_INFORMATION pNotify; - WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; - WatcherWin32* pWatch = tWatch->Watch; size_t offset = 0; + do { + bool skip = false; - if ( dwNumberOfBytesTransfered == 0 ) { - if ( nullptr != pWatch && !pWatch->StopNow ) { - RefreshWatch( tWatch ); - } else { - return; + pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; + offset += pNotify->NextEntryOffset; + int count = + WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL ); + if ( count == 0 ) + continue; + + std::string nfile( count, '\0' ); + + count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, + pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count, + NULL, NULL ); + + if ( FILE_ACTION_MODIFIED == pNotify->Action ) { + FileInfo fifile( std::string( pWatch->DirName ) + nfile ); + + if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime && + pWatch->LastModifiedEvent.file.Size == fifile.Size && + pWatch->LastModifiedEvent.fileName == nfile ) { + skip = true; + } + + pWatch->LastModifiedEvent.fileName = nfile; + pWatch->LastModifiedEvent.file = fifile; } - } + if ( !skip ) { + pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); + } + } while ( pNotify->NextEntryOffset != 0 ); +} + +void WatchCallbackEx( WatcherWin32* pWatch ) { + EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify; + size_t offset = 0; do { bool skip = false; - pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; + pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset]; offset += pNotify->NextEntryOffset; int count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, @@ -56,12 +122,63 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve pWatch->LastModifiedEvent.fileName = nfile; pWatch->LastModifiedEvent.file = fifile; + } else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) { + pWatch->OldFiles.emplace_back( nfile, pNotify->FileId ); + skip = true; + } else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) { + std::string oldFile; + LARGE_INTEGER oldFileId{}; + + for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) { + if ( it->second.QuadPart == pNotify->FileId.QuadPart ) { + oldFile = it->first; + oldFileId = it->second; + it = pWatch->OldFiles.erase( it ); + break; + } + } + + if ( oldFile.empty() ) { + pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED ); + skip = true; + } else { + pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME ); + } } if ( !skip ) { pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); } } while ( pNotify->NextEntryOffset != 0 ); +} + +/// Unpacks events and passes them to a user defined callback. +void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { + if ( NULL == lpOverlapped ) { + return; + } + + WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped; + WatcherWin32* pWatch = tWatch->Watch; + + if ( dwNumberOfBytesTransfered == 0 ) { + if ( nullptr != pWatch && !pWatch->StopNow ) { + /// Missed file actions due to buffer overflowed + std::string dir = pWatch->DirName; + FileSystem::dirRemoveSlashAtEnd( dir ); + pWatch->Listener->handleMissedFileActions( pWatch->ID, dir ); + RefreshWatch( tWatch ); + } else { + return; + } + } + + // Fork watch depending on the Windows API supported + if ( pWatch->Extended ) { + WatchCallbackEx( pWatch ); + } else { + WatchCallbackOld( pWatch ); + } if ( !pWatch->StopNow ) { RefreshWatch( tWatch ); @@ -69,17 +186,40 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve } /// Refreshes the directory monitoring. -bool RefreshWatch( WatcherStructWin32* pWatch ) { - bool bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), - pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, - pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, NULL ) != 0; +RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) { + initReadDirectoryChangesEx(); + + bool bRet = false; + RefreshResult ret = RefreshResult::Failed; + pWatch->Watch->Extended = false; + + if ( pReadDirectoryChangesExW ) { + bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0; + if ( bRet ) { + ret = RefreshResult::SucessEx; + pWatch->Watch->Extended = true; + } + } + + if ( !bRet ) { + bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(), + (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive, + pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, + NULL ) != 0; + + if ( bRet ) + ret = RefreshResult::Success; + } if ( !bRet ) { std::string error = std::to_string( GetLastError() ); Errors::Log::createLastError( Errors::WatcherFailed, error ); } - return bRet; + return ret; } /// Stops monitoring a directory. @@ -91,19 +231,17 @@ void DestroyWatch( WatcherStructWin32* pWatch ) { CloseHandle( pWatch->Watch->DirHandle ); efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); efSAFE_DELETE( pWatch->Watch ); + efSAFE_DELETE( pWatch ); } } /// Starts monitoring a directory. WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) { - WatcherStructWin32* tWatch; - size_t ptrsize = sizeof( *tWatch ); - tWatch = static_cast<WatcherStructWin32*>( - HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize ) ); - + WatcherStructWin32* tWatch = new WatcherStructWin32(); WatcherWin32* pWatch = new WatcherWin32(bufferSize); - tWatch->Watch = pWatch; + if (tWatch) + tWatch->Watch = pWatch; pWatch->DirHandle = CreateFileW( szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -114,14 +252,14 @@ WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, pWatch->NotifyFilter = notifyFilter; pWatch->Recursive = recursive; - if ( RefreshWatch( tWatch ) ) { + if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) { return tWatch; } } CloseHandle( pWatch->DirHandle ); efSAFE_DELETE( pWatch->Watch ); - HeapFree( GetProcessHeap(), 0, tWatch ); + efSAFE_DELETE( tWatch ); return NULL; } diff --git a/dep/efsw/src/efsw/WatcherWin32.hpp b/dep/efsw/src/efsw/WatcherWin32.hpp index ae050b730df..ea1e8e4558c 100644 --- a/dep/efsw/src/efsw/WatcherWin32.hpp +++ b/dep/efsw/src/efsw/WatcherWin32.hpp @@ -22,6 +22,8 @@ namespace efsw { class WatcherWin32; +enum RefreshResult { Failed, Success, SucessEx }; + /// Internal watch data struct WatcherStructWin32 { OVERLAPPED Overlapped; @@ -33,7 +35,7 @@ struct sLastModifiedEvent { std::string fileName; }; -bool RefreshWatch( WatcherStructWin32* pWatch ); +RefreshResult RefreshWatch( WatcherStructWin32* pWatch ); void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); @@ -51,6 +53,7 @@ class WatcherWin32 : public Watcher { lParam( 0 ), NotifyFilter( 0 ), StopNow( false ), + Extended( false ), Watch( NULL ), DirName( NULL ) { Buffer.resize(dwBufferSize); @@ -62,9 +65,11 @@ class WatcherWin32 : public Watcher { LPARAM lParam; DWORD NotifyFilter; bool StopNow; + bool Extended; FileWatcherImpl* Watch; char* DirName; sLastModifiedEvent LastModifiedEvent; + std::vector<std::pair<std::string, LARGE_INTEGER>> OldFiles; }; } // namespace efsw diff --git a/dep/efsw/src/efsw/platform/platformimpl.hpp b/dep/efsw/src/efsw/platform/platformimpl.hpp index 54425806985..f4942418c00 100644 --- a/dep/efsw/src/efsw/platform/platformimpl.hpp +++ b/dep/efsw/src/efsw/platform/platformimpl.hpp @@ -4,13 +4,9 @@ #include <efsw/base.hpp> #if defined( EFSW_PLATFORM_POSIX ) -#include <efsw/platform/posix/ThreadImpl.hpp> -#include <efsw/platform/posix/MutexImpl.hpp> #include <efsw/platform/posix/SystemImpl.hpp> #include <efsw/platform/posix/FileSystemImpl.hpp> #elif EFSW_PLATFORM == EFSW_PLATFORM_WIN32 -#include <efsw/platform/win/ThreadImpl.hpp> -#include <efsw/platform/win/MutexImpl.hpp> #include <efsw/platform/win/SystemImpl.hpp> #include <efsw/platform/win/FileSystemImpl.hpp> #else diff --git a/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp b/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp deleted file mode 100644 index 2233798284c..00000000000 --- a/dep/efsw/src/efsw/platform/posix/MutexImpl.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include <efsw/platform/posix/MutexImpl.hpp> - -#if defined( EFSW_PLATFORM_POSIX ) - -namespace efsw { namespace Platform { - -MutexImpl::MutexImpl() { - pthread_mutexattr_t attributes; - pthread_mutexattr_init( &attributes ); - pthread_mutexattr_settype( &attributes, PTHREAD_MUTEX_RECURSIVE ); - pthread_mutex_init( &mMutex, &attributes ); -} - -MutexImpl::~MutexImpl() { - pthread_mutex_destroy( &mMutex ); -} - -void MutexImpl::lock() { - pthread_mutex_lock( &mMutex ); -} - -void MutexImpl::unlock() { - pthread_mutex_unlock( &mMutex ); -} - -}} // namespace efsw::Platform - -#endif diff --git a/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp b/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp deleted file mode 100644 index a33d827966f..00000000000 --- a/dep/efsw/src/efsw/platform/posix/MutexImpl.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef EFSW_MUTEXIMPLPOSIX_HPP -#define EFSW_MUTEXIMPLPOSIX_HPP - -#include <efsw/base.hpp> - -#if defined( EFSW_PLATFORM_POSIX ) - -#include <pthread.h> - -namespace efsw { namespace Platform { - -class MutexImpl { - public: - MutexImpl(); - - ~MutexImpl(); - - void lock(); - - void unlock(); - - private: - pthread_mutex_t mMutex; -}; - -}} // namespace efsw::Platform - -#endif - -#endif diff --git a/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp b/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp deleted file mode 100644 index 0f96bca7e55..00000000000 --- a/dep/efsw/src/efsw/platform/posix/ThreadImpl.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include <efsw/Thread.hpp> -#include <efsw/platform/posix/ThreadImpl.hpp> - -#if defined( EFSW_PLATFORM_POSIX ) - -#include <cassert> -#include <efsw/Debug.hpp> -#include <iostream> - -namespace efsw { namespace Platform { - -ThreadImpl::ThreadImpl( efsw::Thread* owner ) : mIsActive( false ) { - mIsActive = pthread_create( &mThread, NULL, &ThreadImpl::entryPoint, owner ) == 0; - - if ( !mIsActive ) { - efDEBUG( "Failed to create thread\n" ); - } -} - -ThreadImpl::~ThreadImpl() { - terminate(); -} - -void ThreadImpl::wait() { - // Wait for the thread to finish, no timeout - if ( mIsActive ) { - assert( pthread_equal( pthread_self(), mThread ) == 0 ); - - mIsActive = pthread_join( mThread, NULL ) != 0; - } -} - -void ThreadImpl::terminate() { - if ( mIsActive ) { -#if !defined( __ANDROID__ ) && !defined( ANDROID ) - pthread_cancel( mThread ); -#else - pthread_kill( mThread, SIGUSR1 ); -#endif - - mIsActive = false; - } -} - -void* ThreadImpl::entryPoint( void* userData ) { -// Tell the thread to handle cancel requests immediatly -#ifdef PTHREAD_CANCEL_ASYNCHRONOUS - pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL ); -#endif - - // The Thread instance is stored in the user data - Thread* owner = static_cast<Thread*>( userData ); - - // Forward to the owner - owner->run(); - - return NULL; -} - -}} // namespace efsw::Platform - -#endif diff --git a/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp b/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp deleted file mode 100644 index 2e02f9ac89d..00000000000 --- a/dep/efsw/src/efsw/platform/posix/ThreadImpl.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef EFSW_THREADIMPLPOSIX_HPP -#define EFSW_THREADIMPLPOSIX_HPP - -#include <efsw/base.hpp> - -#if defined( EFSW_PLATFORM_POSIX ) - -#include <efsw/Atomic.hpp> -#include <pthread.h> - -namespace efsw { - -class Thread; - -namespace Platform { - -class ThreadImpl { - public: - explicit ThreadImpl( efsw::Thread* owner ); - - ~ThreadImpl(); - - void wait(); - - void terminate(); - - protected: - static void* entryPoint( void* userData ); - - pthread_t mThread; - Atomic<bool> mIsActive; -}; - -} // namespace Platform -} // namespace efsw - -#endif - -#endif diff --git a/dep/efsw/src/efsw/platform/win/MutexImpl.cpp b/dep/efsw/src/efsw/platform/win/MutexImpl.cpp deleted file mode 100644 index 62b7f836e51..00000000000 --- a/dep/efsw/src/efsw/platform/win/MutexImpl.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include <efsw/platform/win/MutexImpl.hpp> - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -namespace efsw { namespace Platform { - -MutexImpl::MutexImpl() { - InitializeCriticalSection( &mMutex ); -} - -MutexImpl::~MutexImpl() { - DeleteCriticalSection( &mMutex ); -} - -void MutexImpl::lock() { - EnterCriticalSection( &mMutex ); -} - -void MutexImpl::unlock() { - LeaveCriticalSection( &mMutex ); -} - -}} // namespace efsw::Platform - -#endif diff --git a/dep/efsw/src/efsw/platform/win/MutexImpl.hpp b/dep/efsw/src/efsw/platform/win/MutexImpl.hpp deleted file mode 100644 index 7b064920f81..00000000000 --- a/dep/efsw/src/efsw/platform/win/MutexImpl.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef EFSW_MUTEXIMPLWIN_HPP -#define EFSW_MUTEXIMPLWIN_HPP - -#include <efsw/base.hpp> - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include <windows.h> - -namespace efsw { namespace Platform { - -class MutexImpl { - public: - MutexImpl(); - - ~MutexImpl(); - - void lock(); - - void unlock(); - - private: - CRITICAL_SECTION mMutex; -}; - -}} // namespace efsw::Platform - -#endif - -#endif diff --git a/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp b/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp deleted file mode 100644 index 463934c9009..00000000000 --- a/dep/efsw/src/efsw/platform/win/ThreadImpl.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include <assert.h> -#include <efsw/Thread.hpp> -#include <efsw/platform/win/ThreadImpl.hpp> - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -#include <efsw/Debug.hpp> - -namespace efsw { namespace Platform { - -ThreadImpl::ThreadImpl( efsw::Thread* owner ) { - mThread = reinterpret_cast<HANDLE>( - _beginthreadex( NULL, 0, &ThreadImpl::entryPoint, owner, 0, &mThreadId ) ); - - if ( !mThread ) { - efDEBUG( "Failed to create thread\n" ); - } -} - -ThreadImpl::~ThreadImpl() { - if ( mThread ) { - CloseHandle( mThread ); - } -} - -void ThreadImpl::wait() { - // Wait for the thread to finish, no timeout - if ( mThread ) { - assert( mThreadId != GetCurrentThreadId() ); // A thread cannot wait for itself! - - WaitForSingleObject( mThread, INFINITE ); - } -} - -void ThreadImpl::terminate() { - if ( mThread ) { - TerminateThread( mThread, 0 ); - } -} - -unsigned int __stdcall ThreadImpl::entryPoint( void* userData ) { - // The Thread instance is stored in the user data - Thread* owner = static_cast<Thread*>( userData ); - - // Forward to the owner - owner->run(); - - // Optional, but it is cleaner - _endthreadex( 0 ); - - return 0; -} - -}} // namespace efsw::Platform - -#endif diff --git a/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp b/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp deleted file mode 100644 index 455f24c2783..00000000000 --- a/dep/efsw/src/efsw/platform/win/ThreadImpl.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef EFSW_THREADIMPLWIN_HPP -#define EFSW_THREADIMPLWIN_HPP - -#include <efsw/base.hpp> - -#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include <process.h> -#include <windows.h> - -namespace efsw { - -class Thread; - -namespace Platform { - -class ThreadImpl { - public: - explicit ThreadImpl( efsw::Thread* owner ); - - ~ThreadImpl(); - - void wait(); - - void terminate(); - - protected: - static unsigned int __stdcall entryPoint( void* userData ); - - HANDLE mThread; - unsigned int mThreadId; -}; - -} // namespace Platform -} // namespace efsw - -#endif - -#endif diff --git a/dep/efsw/src/test/efsw-test.c b/dep/efsw/src/test/efsw-test.c index 54a3e21bba5..143f35f87ec 100644 --- a/dep/efsw/src/test/efsw-test.c +++ b/dep/efsw/src/test/efsw-test.c @@ -8,9 +8,9 @@ #include <time.h> #ifdef _WIN32 - #include <Windows.h> +#include <Windows.h> #else - #include <unistd.h> +#include <unistd.h> #endif const char PATH_SEPARATOR = @@ -32,10 +32,10 @@ void sleepMsecs( int msecs ) { Sleep( msecs ); #else sleep( msecs ); -#endif +#endif } -const char * getActionName( enum efsw_action action ) { +const char* getActionName( enum efsw_action action ) { switch ( action ) { case EFSW_ADD: return "Add"; @@ -50,16 +50,15 @@ const char * getActionName( enum efsw_action action ) { } } -void handleFileAction( efsw_watcher watcher, efsw_watchid watchid, - const char* dir, const char* filename, - enum efsw_action action, const char* oldFilename, - void* param ) { +void handleFileAction( efsw_watcher watcher, efsw_watchid watchid, const char* dir, + const char* filename, enum efsw_action action, const char* oldFilename, + void* param ) { if ( strlen( oldFilename ) == 0 ) { - printf( "Watch ID %ld DIR (%s) FILE (%s) has event %s\n", - watchid, dir, filename, getActionName( action )); + printf( "Watch ID %ld DIR (%s) FILE (%s) has event %s\n", watchid, dir, filename, + getActionName( action ) ); } else { - printf( "Watch ID %ld DIR (%s) FILE (from file %s to %s) has event %s\n", - watchid, dir, oldFilename, filename, getActionName( action )); + printf( "Watch ID %ld DIR (%s) FILE (from file %s to %s) has event %s\n", watchid, dir, + oldFilename, filename, getActionName( action ) ); } } @@ -87,17 +86,17 @@ int main( int argc, char** argv ) { signal( SIGINT, sigend ); signal( SIGTERM, sigend ); - printf("Press ^C to exit demo\n"); + printf( "Press ^C to exit demo\n" ); bool commonTest = true; bool useGeneric = false; - char *path = 0; + char* path = 0; if ( argc >= 2 ) { path = argv[1]; struct stat s; - if( stat(path,&s) == 0 && (s.st_mode & S_IFDIR) == S_IFDIR ) { + if ( stat( path, &s ) == 0 && ( s.st_mode & S_IFDIR ) == S_IFDIR ) { commonTest = false; } @@ -115,24 +114,25 @@ int main( int argc, char** argv ) { if ( commonTest ) { char cwd[256]; - getcwd( cwd, sizeof(cwd) ); + getcwd( cwd, sizeof( cwd ) ); printf( "CurPath: %s\n", cwd ); /// starts watching efsw_watch( fileWatcher ); /// add a watch to the system - char path1[256]; - sprintf(path1, "%s%ctest", cwd, PATH_SEPARATOR ); - handleWatchID( efsw_addwatch_withoptions( fileWatcher, path1, handleFileAction, true, 0, 0, 0 ) ); + char path1[512]; + snprintf( path1, sizeof( path1 ), "%s%ctest", cwd, PATH_SEPARATOR ); + handleWatchID( efsw_addwatch_withoptions( fileWatcher, path1, handleFileAction, true, 0, 0, + 0, NULL ) ); /// adds another watch after started watching... sleepMsecs( 100 ); - char path2[256]; - sprintf(path2, "%s%ctest2", cwd, PATH_SEPARATOR ); - efsw_watchid watchID = handleWatchID( - efsw_addwatch_withoptions( fileWatcher, path2, handleFileAction, true, 0, 0, 0 ) ); + char path2[512]; + snprintf( path2, sizeof( path2 ), "%s%ctest2", cwd, PATH_SEPARATOR ); + efsw_watchid watchID = handleWatchID( efsw_addwatch_withoptions( + fileWatcher, path2, handleFileAction, true, 0, 0, 0, NULL ) ); /// delete the watch if ( watchID > 0 ) { @@ -159,6 +159,6 @@ int main( int argc, char** argv ) { } efsw_release( fileWatcher ); - + return 0; } diff --git a/dep/efsw/src/test/efsw-test.cpp b/dep/efsw/src/test/efsw-test.cpp index d51d68d7599..bf1fc6481bf 100644 --- a/dep/efsw/src/test/efsw-test.cpp +++ b/dep/efsw/src/test/efsw-test.cpp @@ -99,7 +99,7 @@ int main( int argc, char** argv ) { std::cout << "CurPath: " << CurPath.c_str() << std::endl; - /// starts watching + /// starts watching fileWatcher.watch(); /// add a watch to the system |
