| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "Platform/BsFolderMonitor.h"
- #include "FileSystem/BsFileSystem.h"
- #include "Error/BsException.h"
- #include <corecrt_io.h>
- namespace bs
- {
- class WorkerFunc
- {
- public:
- WorkerFunc(FolderMonitor* owner);
- void operator()();
- private:
- FolderMonitor* mOwner;
- };
- struct FolderMonitor::FolderWatchInfo
- {
- FolderWatchInfo(const Path& folderToMonitor, int inHandle, bool monitorSubdirectories, UINT32 monitorFlags);
- ~FolderWatchInfo();
- void startMonitor();
- void stopMonitor();
- Path mFolderToMonitor;
- int mDirHandle;
- bool mMonitorSubdirectories;
- UINT32 mMonitorFlags;
- };
- FolderMonitor::FolderWatchInfo::FolderWatchInfo(const Path& folderToMonitor, int inHandle, bool monitorSubdirectories, UINT32 monitorFlags)
- :mFolderToMonitor(folderToMonitor), mDirHandle(0), mMonitorSubdirectories(monitorSubdirectories), mMonitorFlags(monitorFlags)
- { }
- FolderMonitor::FolderWatchInfo::~FolderWatchInfo()
- {
- stopMonitor();
- }
- void FolderMonitor::FolderWatchInfo::startMonitor()
- {
- }
- void FolderMonitor::FolderWatchInfo::stopMonitor()
- {
- }
- class FolderMonitor::FileNotifyInfo
- {
- };
- enum class FileActionType
- {
- Added,
- Removed,
- Modified,
- Renamed
- };
- struct FileAction
- {
- static FileAction* createAdded(const WString& fileName)
- {
- UINT8* bytes = (UINT8*)bs_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
- FileAction* action = (FileAction*)bytes;
- bytes += sizeof(FileAction);
- action->oldName = nullptr;
- action->newName = (WString::value_type*)bytes;
- action->type = FileActionType::Added;
- memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
- action->newName[fileName.size()] = L'\0';
- action->lastSize = 0;
- action->checkForWriteStarted = false;
- return action;
- }
- static FileAction* createRemoved(const WString& fileName)
- {
- UINT8* bytes = (UINT8*)bs_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
- FileAction* action = (FileAction*)bytes;
- bytes += sizeof(FileAction);
- action->oldName = nullptr;
- action->newName = (WString::value_type*)bytes;
- action->type = FileActionType::Removed;
- memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
- action->newName[fileName.size()] = L'\0';
- action->lastSize = 0;
- action->checkForWriteStarted = false;
- return action;
- }
- static FileAction* createModified(const WString& fileName)
- {
- UINT8* bytes = (UINT8*)bs_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
- FileAction* action = (FileAction*)bytes;
- bytes += sizeof(FileAction);
- action->oldName = nullptr;
- action->newName = (WString::value_type*)bytes;
- action->type = FileActionType::Modified;
- memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
- action->newName[fileName.size()] = L'\0';
- action->lastSize = 0;
- action->checkForWriteStarted = false;
- return action;
- }
- static FileAction* createRenamed(const WString& oldFilename, const WString& newfileName)
- {
- UINT8* bytes = (UINT8*)bs_alloc((UINT32)(sizeof(FileAction) +
- (oldFilename.size() + newfileName.size() + 2) * sizeof(WString::value_type)));
- FileAction* action = (FileAction*)bytes;
- bytes += sizeof(FileAction);
- action->oldName = (WString::value_type*)bytes;
- bytes += (oldFilename.size() + 1) * sizeof(WString::value_type);
- action->newName = (WString::value_type*)bytes;
- action->type = FileActionType::Modified;
- memcpy(action->oldName, oldFilename.data(), oldFilename.size() * sizeof(WString::value_type));
- action->oldName[oldFilename.size()] = L'\0';
- memcpy(action->newName, newfileName.data(), newfileName.size() * sizeof(WString::value_type));
- action->newName[newfileName.size()] = L'\0';
- action->lastSize = 0;
- action->checkForWriteStarted = false;
- return action;
- }
- static void destroy(FileAction* action)
- {
- bs_free(action);
- }
- WString::value_type* oldName;
- WString::value_type* newName;
- FileActionType type;
- UINT64 lastSize;
- bool checkForWriteStarted;
- };
- struct FolderMonitor::Pimpl
- {
- Vector<FolderWatchInfo*> mFoldersToWatch;
- Queue<FileAction*> mFileActions;
- List<FileAction*> mActiveFileActions;
- int inHandle;
- Mutex mMainMutex;
- Thread* mWorkerThread;
- };
- FolderMonitor::FolderMonitor()
- {
- mPimpl = bs_new<Pimpl>();
- mPimpl->mWorkerThread = nullptr;
- mPimpl->inHandle = 0;
- }
- FolderMonitor::~FolderMonitor()
- {
- stopMonitorAll();
- // No need for mutex since we know worker thread is shut down by now
- while (!mPimpl->mFileActions.empty())
- {
- FileAction* action = mPimpl->mFileActions.front();
- mPimpl->mFileActions.pop();
- FileAction::destroy(action);
- }
- bs_delete(mPimpl);
- }
- void FolderMonitor::startMonitor(const Path& folderPath, bool subdirectories, FolderChange changeFilter)
- {
- if(!FileSystem::isDirectory(folderPath))
- {
- LOGERR("Provided path \"" + folderPath.toString() + "\" is not a directory");
- return;
- }
- UINT32 filterFlags = 0;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::FileName) != 0)
- filterFlags |= IN_MOVE | IN_CREATE | IN_DELETE;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::DirName) != 0)
- filterFlags |= IN_MOVE | IN_CREATE | IN_DELETE;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::Attributes) != 0)
- filterFlags |= IN_ATTRIB;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::Size) != 0)
- filterFlags |= IN_ATTRIB | IN_MODIFY | IN_CREATE | IN_DELETE;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::LastWrite) != 0)
- filterFlags |= IN_ATTRIB | IN_MODIFY | IN_CLOSE_WRITE | IN_CREATE | IN_MOVED_TO;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::LastAccess) != 0)
- filterFlags |= IN_ATTRIB | IN_ACCESS;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::Creation) != 0)
- filterFlags |= IN_CREATE;
- if((((UINT32)changeFilter) & (UINT32)bs::FolderChange::Security) != 0)
- filterFlags |= IN_ATTRIB;
- mPimpl->inHandle = inotify_init();
- mPimpl->mFoldersToWatch.push_back(bs_new<FolderWatchInfo>(folderPath, subdirectories, filterFlags));
- FolderWatchInfo* watchInfo = mPimpl->mFoldersToWatch.back();
- if(mPimpl->mWorkerThread == nullptr)
- {
- mPimpl->mWorkerThread = bs_new<Thread>(std::bind(&FolderMonitor::workerThreadMain, this));
- if(mPimpl->mWorkerThread == nullptr)
- {
- mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
- bs_delete(watchInfo);
- BS_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
- }
- }
- if(mPimpl->mWorkerThread != nullptr)
- {
- watchInfo->startMonitor();
- }
- else
- {
- mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
- bs_delete(watchInfo);
- BS_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
- }
- }
- void FolderMonitor::stopMonitor(const Path& folderPath)
- {
- auto findIter = std::find_if(mPimpl->mFoldersToWatch.begin(), mPimpl->mFoldersToWatch.end(),
- [&](const FolderWatchInfo* x) { return x->mFolderToMonitor == folderPath; });
- if(findIter != mPimpl->mFoldersToWatch.end())
- {
- FolderWatchInfo* watchInfo = *findIter;
- watchInfo->stopMonitor();
- bs_delete(watchInfo);
- mPimpl->mFoldersToWatch.erase(findIter);
- }
- if(mPimpl->mFoldersToWatch.size() == 0)
- stopMonitorAll();
- }
- void FolderMonitor::stopMonitorAll()
- {
- for(auto& watchInfo : mPimpl->mFoldersToWatch)
- {
- watchInfo->stopMonitor();
- {
- // Note: Need this mutex to ensure worker thread is done with watchInfo.
- // Even though we wait for a condition variable from the worker thread in stopMonitor,
- // that doesn't mean the worker thread is done with the condition variable
- // (which is stored inside watchInfo)
- Lock lock(mPimpl->mMainMutex);
- bs_delete(watchInfo);
- }
- }
- mPimpl->mFoldersToWatch.clear();
- if(mPimpl->mWorkerThread != nullptr)
- {
- mPimpl->mWorkerThread->join();
- bs_delete(mPimpl->mWorkerThread);
- mPimpl->mWorkerThread = nullptr;
- }
- if(mPimpl->inHandle != 0)
- {
- close(mPimpl->inHandle);
- mPimpl->inHandle = 0;
- }
- }
- void FolderMonitor::workerThreadMain()
- {
- }
- void FolderMonitor::handleNotifications(FileNotifyInfo& notifyInfo, FolderWatchInfo& watchInfo)
- {
- }
- void FolderMonitor::_update()
- {
- {
- Lock lock(mPimpl->mMainMutex);
- while (!mPimpl->mFileActions.empty())
- {
- FileAction* action = mPimpl->mFileActions.front();
- mPimpl->mFileActions.pop();
- mPimpl->mActiveFileActions.push_back(action);
- }
- }
- for (auto iter = mPimpl->mActiveFileActions.begin(); iter != mPimpl->mActiveFileActions.end();)
- {
- FileAction* action = *iter;
-
- // Reported file actions might still be in progress (i.e. something might still be writing to those files).
- // Sadly there doesn't seem to be a way to properly determine when those files are done being written, so instead
- // we check for at least a couple of frames if the file's size hasn't changed before reporting a file action.
- // This takes care of most of the issues and avoids reporting partially written files in almost all cases.
- if (FileSystem::exists(action->newName))
- {
- UINT64 size = FileSystem::getFileSize(action->newName);
- if (!action->checkForWriteStarted)
- {
- action->checkForWriteStarted = true;
- action->lastSize = size;
- ++iter;
- continue;
- }
- else
- {
- if (action->lastSize != size)
- {
- action->lastSize = size;
- ++iter;
- continue;
- }
- }
- }
- switch (action->type)
- {
- case FileActionType::Added:
- if (!onAdded.empty())
- onAdded(Path(action->newName));
- break;
- case FileActionType::Removed:
- if (!onRemoved.empty())
- onRemoved(Path(action->newName));
- break;
- case FileActionType::Modified:
- if (!onModified.empty())
- onModified(Path(action->newName));
- break;
- case FileActionType::Renamed:
- if (!onRenamed.empty())
- onRenamed(Path(action->oldName), Path(action->newName));
- break;
- }
- mPimpl->mActiveFileActions.erase(iter++);
- FileAction::destroy(action);
- }
- }
- }
|