Files
TrinityCore/dep/efsw/src/efsw/FileWatcherWin32.cpp
Naios dc4ff7a9f2 Dep: Add efsw (entropia file system watcher)
* Uses system API's which reduces the overhead
  instead of checking periodically for changes.
* Will be used in the hotswap system to reload
  shared libraries on changes.
2016-08-08 21:51:31 +02:00

292 lines
5.9 KiB
C++

#include <efsw/FileWatcherWin32.hpp>
#include <efsw/FileSystem.hpp>
#include <efsw/System.hpp>
#include <efsw/String.hpp>
#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
namespace efsw
{
FileWatcherWin32::FileWatcherWin32( FileWatcher * parent ) :
FileWatcherImpl( parent ),
mLastWatchID(0),
mThread( NULL )
{
mInitOK = true;
}
FileWatcherWin32::~FileWatcherWin32()
{
WatchVector::iterator iter = mWatches.begin();
mWatchesLock.lock();
for(; iter != mWatches.end(); ++iter)
{
DestroyWatch((*iter));
}
mHandles.clear();
mWatches.clear();
mInitOK = false;
mWatchesLock.unlock();
efSAFE_DELETE( mThread );
}
WatchID FileWatcherWin32::addWatch(const std::string& directory, FileWatchListener* watcher, bool recursive)
{
std::string dir( directory );
FileInfo fi( dir );
if ( !fi.isDirectory() )
{
return Errors::Log::createLastError( Errors::FileNotFound, dir );
}
else if ( !fi.isReadable() )
{
return Errors::Log::createLastError( Errors::FileNotReadable, dir );
}
FileSystem::dirAddSlashAtEnd( dir );
WatchID watchid = ++mLastWatchID;
mWatchesLock.lock();
WatcherStructWin32 * watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(), recursive, FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE
);
if( NULL == watch )
{
return Errors::Log::createLastError( Errors::FileNotFound, dir );
}
if ( pathInWatches( dir ) )
{
return Errors::Log::createLastError( Errors::FileRepeated, dir );
}
// Add the handle to the handles vector
watch->Watch->ID = watchid;
watch->Watch->Watch = this;
watch->Watch->Listener = watcher;
watch->Watch->DirName = new char[dir.length()+1];
strcpy(watch->Watch->DirName, dir.c_str());
mHandles.push_back( watch->Watch->DirHandle );
mWatches.push_back( watch );
mWatchesLock.unlock();
return watchid;
}
void FileWatcherWin32::removeWatch(const std::string& directory)
{
mWatchesLock.lock();
WatchVector::iterator iter = mWatches.begin();
for(; iter != mWatches.end(); ++iter)
{
if(directory == (*iter)->Watch->DirName)
{
removeWatch((*iter)->Watch->ID);
return;
}
}
mWatchesLock.unlock();
}
void FileWatcherWin32::removeWatch(WatchID watchid)
{
mWatchesLock.lock();
WatchVector::iterator iter = mWatches.begin();
WatcherStructWin32* watch = NULL;
for(; iter != mWatches.end(); ++iter)
{
// Find the watch ID
if ( (*iter)->Watch->ID == watchid )
{
watch = (*iter);
mWatches.erase( iter );
// Remove handle from the handle vector
HandleVector::iterator it = mHandles.begin();
for ( ; it != mHandles.end(); it++ )
{
if ( watch->Watch->DirHandle == (*it) )
{
mHandles.erase( it );
break;
}
}
DestroyWatch(watch);
break;
}
}
mWatchesLock.unlock();
}
void FileWatcherWin32::watch()
{
if ( NULL == mThread )
{
mThread = new Thread( &FileWatcherWin32::run, this );
mThread->launch();
}
}
void FileWatcherWin32::run()
{
if ( mHandles.empty() )
{
return;
}
do
{
if ( !mHandles.empty() )
{
mWatchesLock.lock();
for ( std::size_t i = 0; i < mWatches.size(); i++ )
{
WatcherStructWin32 * watch = mWatches[ i ];
// If the overlapped struct was cancelled ( because the creator thread doesn't exists anymore ),
// we recreate the overlapped in the current thread and refresh the watch
if ( /*STATUS_CANCELED*/0xC0000120 == watch->Overlapped.Internal )
{
watch->Overlapped = OVERLAPPED();
RefreshWatch(watch);
}
// First ensure that the handle is the same, this means that the watch was not removed.
if ( HasOverlappedIoCompleted( &watch->Overlapped ) && mHandles[ i ] == watch->Watch->DirHandle )
{
DWORD bytes;
if ( GetOverlappedResult( watch->Watch->DirHandle, &watch->Overlapped, &bytes, FALSE ) )
{
WatchCallback( ERROR_SUCCESS, bytes, &watch->Overlapped );
}
}
}
mWatchesLock.unlock();
if ( mInitOK )
{
System::sleep( 10 );
}
}
else
{
// Wait for a new handle to be added
System::sleep( 10 );
}
} while ( mInitOK );
}
void FileWatcherWin32::handleAction(Watcher* watch, const std::string& filename, unsigned long action, std::string oldFilename)
{
Action fwAction;
switch(action)
{
case FILE_ACTION_RENAMED_OLD_NAME:
watch->OldFileName = filename;
return;
case FILE_ACTION_ADDED:
fwAction = Actions::Add;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
{
fwAction = Actions::Moved;
std::string fpath( watch->Directory + filename );
// Update the directory path
if ( watch->Recursive && FileSystem::isDirectory( fpath ) )
{
// Update the new directory path
std::string opath( watch->Directory + watch->OldFileName );
FileSystem::dirAddSlashAtEnd( opath );
FileSystem::dirAddSlashAtEnd( fpath );
for ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( (*it)->Watch->Directory == opath )
{
(*it)->Watch->Directory = fpath;
break;
}
}
}
watch->Listener->handleFileAction(watch->ID, static_cast<WatcherWin32*>( watch )->DirName, filename, fwAction, watch->OldFileName);
return;
}
case FILE_ACTION_REMOVED:
fwAction = Actions::Delete;
break;
case FILE_ACTION_MODIFIED:
fwAction = Actions::Modified;
break;
};
watch->Listener->handleFileAction(watch->ID, static_cast<WatcherWin32*>( watch )->DirName, filename, fwAction);
}
std::list<std::string> FileWatcherWin32::directories()
{
std::list<std::string> dirs;
mWatchesLock.lock();
for ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
dirs.push_back( std::string( (*it)->Watch->DirName ) );
}
mWatchesLock.unlock();
return dirs;
}
bool FileWatcherWin32::pathInWatches( const std::string& path )
{
for ( WatchVector::iterator it = mWatches.begin(); it != mWatches.end(); it++ )
{
if ( (*it)->Watch->DirName == path )
{
return true;
}
}
return false;
}
}
#endif