CmWin32FolderMonitor.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. #include "CmWin32FolderMonitor.h"
  2. #include "CmFileSystem.h"
  3. #include "CmException.h"
  4. #include "CmPath.h"
  5. #include "CmDebug.h"
  6. #include <windows.h>
  7. namespace BansheeEngine
  8. {
  9. enum class MonitorState
  10. {
  11. Inactive,
  12. Starting,
  13. Monitoring,
  14. Shutdown,
  15. Shutdown2
  16. };
  17. class WorkerFunc
  18. {
  19. public:
  20. WorkerFunc(FolderMonitor* owner);
  21. void operator()();
  22. private:
  23. FolderMonitor* mOwner;
  24. };
  25. struct FolderMonitor::FolderWatchInfo
  26. {
  27. FolderWatchInfo(const Path& folderToMonitor, HANDLE dirHandle, bool monitorSubdirectories, DWORD monitorFlags);
  28. ~FolderWatchInfo();
  29. void startMonitor(HANDLE compPortHandle);
  30. void stopMonitor(HANDLE compPortHandle);
  31. static const UINT32 READ_BUFFER_SIZE = 65536;
  32. Path mFolderToMonitor;
  33. HANDLE mDirHandle;
  34. OVERLAPPED mOverlapped;
  35. MonitorState mState;
  36. UINT8 mBuffer[READ_BUFFER_SIZE];
  37. DWORD mBufferSize;
  38. bool mMonitorSubdirectories;
  39. DWORD mMonitorFlags;
  40. DWORD mReadError;
  41. WString mCachedOldFileName; // Used during rename notifications as they are handled in two steps
  42. CM_MUTEX(mStatusMutex)
  43. CM_THREAD_SYNCHRONISER(mStartStopEvent)
  44. };
  45. FolderMonitor::FolderWatchInfo::FolderWatchInfo(const Path& folderToMonitor, HANDLE dirHandle, bool monitorSubdirectories, DWORD monitorFlags)
  46. :mFolderToMonitor(folderToMonitor), mDirHandle(dirHandle), mState(MonitorState::Inactive), mBufferSize(0),
  47. mMonitorSubdirectories(monitorSubdirectories), mMonitorFlags(monitorFlags), mReadError(0)
  48. {
  49. memset(&mOverlapped, 0, sizeof(mOverlapped));
  50. }
  51. FolderMonitor::FolderWatchInfo::~FolderWatchInfo()
  52. {
  53. assert(mState == MonitorState::Inactive);
  54. stopMonitor(0);
  55. }
  56. void FolderMonitor::FolderWatchInfo::startMonitor(HANDLE compPortHandle)
  57. {
  58. if(mState != MonitorState::Inactive)
  59. return; // Already monitoring
  60. {
  61. CM_LOCK_MUTEX_NAMED(mStatusMutex, lock);
  62. mState = MonitorState::Starting;
  63. PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
  64. while(mState != MonitorState::Monitoring)
  65. CM_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
  66. }
  67. if(mReadError != ERROR_SUCCESS)
  68. {
  69. {
  70. CM_LOCK_MUTEX(mStatusMutex);
  71. mState = MonitorState::Inactive;
  72. }
  73. CM_EXCEPT(InternalErrorException, "Failed to start folder monitor on folder \"" +
  74. mFolderToMonitor.toString() + "\" because ReadDirectoryChangesW failed.");
  75. }
  76. }
  77. void FolderMonitor::FolderWatchInfo::stopMonitor(HANDLE compPortHandle)
  78. {
  79. if(mState != MonitorState::Inactive)
  80. {
  81. CM_LOCK_MUTEX_NAMED(mStatusMutex, lock);
  82. mState = MonitorState::Shutdown;
  83. PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
  84. while(mState != MonitorState::Inactive)
  85. CM_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
  86. }
  87. if(mDirHandle != INVALID_HANDLE_VALUE)
  88. {
  89. CloseHandle(mDirHandle);
  90. mDirHandle = INVALID_HANDLE_VALUE;
  91. }
  92. }
  93. class FolderMonitor::FileNotifyInfo
  94. {
  95. public:
  96. FileNotifyInfo(UINT8* notifyBuffer, DWORD bufferSize)
  97. :mBuffer(notifyBuffer), mBufferSize(bufferSize)
  98. {
  99. mCurrentRecord = (PFILE_NOTIFY_INFORMATION)mBuffer;
  100. }
  101. bool getNext();
  102. DWORD getAction() const;
  103. WString getFileName() const;
  104. WString getFileNameWithPath(const Path& rootPath) const;
  105. protected:
  106. UINT8* mBuffer;
  107. DWORD mBufferSize;
  108. PFILE_NOTIFY_INFORMATION mCurrentRecord;
  109. };
  110. bool FolderMonitor::FileNotifyInfo::getNext()
  111. {
  112. if(mCurrentRecord && mCurrentRecord->NextEntryOffset != 0)
  113. {
  114. PFILE_NOTIFY_INFORMATION oldRecord = mCurrentRecord;
  115. mCurrentRecord = (PFILE_NOTIFY_INFORMATION) ((UINT8*)mCurrentRecord + mCurrentRecord->NextEntryOffset);
  116. if((DWORD)((UINT8*)mCurrentRecord - mBuffer) > mBufferSize)
  117. {
  118. // Gone out of range, something bad happened
  119. assert(false);
  120. mCurrentRecord = oldRecord;
  121. }
  122. return (mCurrentRecord != oldRecord);
  123. }
  124. return false;
  125. }
  126. DWORD FolderMonitor::FileNotifyInfo::getAction() const
  127. {
  128. assert(mCurrentRecord != nullptr);
  129. if(mCurrentRecord)
  130. return mCurrentRecord->Action;
  131. return 0;
  132. }
  133. WString FolderMonitor::FileNotifyInfo::getFileName() const
  134. {
  135. if(mCurrentRecord)
  136. {
  137. wchar_t fileNameBuffer[32768 + 1] = {0};
  138. memcpy(fileNameBuffer, mCurrentRecord->FileName,
  139. std::min(DWORD(32768 * sizeof(wchar_t)), mCurrentRecord->FileNameLength));
  140. return WString(fileNameBuffer);
  141. }
  142. return WString();
  143. }
  144. WString FolderMonitor::FileNotifyInfo::getFileNameWithPath(const Path& rootPath) const
  145. {
  146. Path fullPath = rootPath;
  147. return fullPath.append(getFileName()).toWString();
  148. }
  149. enum class FileActionType
  150. {
  151. Added,
  152. Removed,
  153. Modified,
  154. Renamed
  155. };
  156. struct FileAction
  157. {
  158. static FileAction* createAdded(const WString& fileName)
  159. {
  160. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  161. FileAction* action = (FileAction*)bytes;
  162. bytes += sizeof(FileAction);
  163. action->oldName = nullptr;
  164. action->newName = (WString::value_type*)bytes;
  165. action->type = FileActionType::Added;
  166. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  167. action->newName[fileName.size()] = L'\0';
  168. return action;
  169. }
  170. static FileAction* createRemoved(const WString& fileName)
  171. {
  172. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  173. FileAction* action = (FileAction*)bytes;
  174. bytes += sizeof(FileAction);
  175. action->oldName = nullptr;
  176. action->newName = (WString::value_type*)bytes;
  177. action->type = FileActionType::Removed;
  178. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  179. action->newName[fileName.size()] = L'\0';
  180. return action;
  181. }
  182. static FileAction* createModified(const WString& fileName)
  183. {
  184. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) + (fileName.size() + 1) * sizeof(WString::value_type)));
  185. FileAction* action = (FileAction*)bytes;
  186. bytes += sizeof(FileAction);
  187. action->oldName = nullptr;
  188. action->newName = (WString::value_type*)bytes;
  189. action->type = FileActionType::Modified;
  190. memcpy(action->newName, fileName.data(), fileName.size() * sizeof(WString::value_type));
  191. action->newName[fileName.size()] = L'\0';
  192. return action;
  193. }
  194. static FileAction* createRenamed(const WString& oldFilename, const WString& newfileName)
  195. {
  196. UINT8* bytes = (UINT8*)cm_alloc((UINT32)(sizeof(FileAction) +
  197. (oldFilename.size() + newfileName.size() + 2) * sizeof(WString::value_type)));
  198. FileAction* action = (FileAction*)bytes;
  199. bytes += sizeof(FileAction);
  200. action->oldName = (WString::value_type*)bytes;
  201. bytes += (oldFilename.size() + 1) * sizeof(WString::value_type);
  202. action->newName = (WString::value_type*)bytes;
  203. action->type = FileActionType::Modified;
  204. memcpy(action->oldName, oldFilename.data(), oldFilename.size() * sizeof(WString::value_type));
  205. action->oldName[oldFilename.size()] = L'\0';
  206. memcpy(action->newName, newfileName.data(), newfileName.size() * sizeof(WString::value_type));
  207. action->newName[newfileName.size()] = L'\0';
  208. return action;
  209. }
  210. static void destroy(FileAction* action)
  211. {
  212. cm_free(action);
  213. }
  214. WString::value_type* oldName;
  215. WString::value_type* newName;
  216. FileActionType type;
  217. };
  218. struct FolderMonitor::Pimpl
  219. {
  220. Vector<FolderWatchInfo*>::type mFoldersToWatch;
  221. HANDLE mCompPortHandle;
  222. Queue<FileAction*>::type mFileActions;
  223. Queue<FileAction*>::type mActiveFileActions;
  224. CM_MUTEX(mMainMutex);
  225. CM_THREAD_TYPE* mWorkerThread;
  226. };
  227. FolderMonitor::FolderMonitor()
  228. {
  229. mPimpl = cm_new<Pimpl>();
  230. mPimpl->mWorkerThread = nullptr;
  231. mPimpl->mCompPortHandle = nullptr;
  232. }
  233. FolderMonitor::~FolderMonitor()
  234. {
  235. stopMonitorAll();
  236. // No need for mutex since we know worker thread is shut down by now
  237. while(!mPimpl->mFileActions.empty())
  238. {
  239. FileAction* action = mPimpl->mFileActions.front();
  240. mPimpl->mFileActions.pop();
  241. FileAction::destroy(action);
  242. }
  243. cm_delete(mPimpl);
  244. }
  245. void FolderMonitor::startMonitor(const Path& folderPath, bool subdirectories, FolderChange changeFilter)
  246. {
  247. if(!FileSystem::isDirectory(folderPath))
  248. {
  249. CM_EXCEPT(InvalidParametersException, "Provided path \"" + folderPath.toString() + "\" is not a directory");
  250. }
  251. WString extendedFolderPath = L"\\\\?\\" + folderPath.toWString(Path::PathType::Windows);
  252. HANDLE dirHandle = CreateFileW(extendedFolderPath.c_str(), FILE_LIST_DIRECTORY,
  253. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
  254. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
  255. if(dirHandle == INVALID_HANDLE_VALUE)
  256. {
  257. CM_EXCEPT(InternalErrorException, "Failed to open folder \"" + folderPath.toString() + "\" for monitoring. Error code: " + toString((UINT64)GetLastError()));
  258. }
  259. DWORD filterFlags = 0;
  260. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::FileName) != 0)
  261. filterFlags |= FILE_NOTIFY_CHANGE_FILE_NAME;
  262. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::DirName) != 0)
  263. filterFlags |= FILE_NOTIFY_CHANGE_DIR_NAME;
  264. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Attributes) != 0)
  265. filterFlags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  266. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Size) != 0)
  267. filterFlags |= FILE_NOTIFY_CHANGE_SIZE;
  268. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::LastWrite) != 0)
  269. filterFlags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  270. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::LastAccess) != 0)
  271. filterFlags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  272. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Creation) != 0)
  273. filterFlags |= FILE_NOTIFY_CHANGE_CREATION;
  274. if((((UINT32)changeFilter) & (UINT32)BansheeEngine::FolderChange::Security) != 0)
  275. filterFlags |= FILE_NOTIFY_CHANGE_SECURITY;
  276. mPimpl->mFoldersToWatch.push_back(cm_new<FolderWatchInfo>(folderPath, dirHandle, subdirectories, filterFlags));
  277. FolderWatchInfo* watchInfo = mPimpl->mFoldersToWatch.back();
  278. mPimpl->mCompPortHandle = CreateIoCompletionPort(dirHandle, mPimpl->mCompPortHandle, (DWORD)watchInfo, 0);
  279. if(mPimpl->mCompPortHandle == nullptr)
  280. {
  281. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  282. cm_delete(watchInfo);
  283. CM_EXCEPT(InternalErrorException, "Failed to open completition port for folder monitoring. Error code: " + toString((UINT64)GetLastError()));
  284. }
  285. if(mPimpl->mWorkerThread == nullptr)
  286. {
  287. CM_THREAD_CREATE(t, (std::bind(&FolderMonitor::workerThreadMain, this)));
  288. mPimpl->mWorkerThread = t;
  289. if(mPimpl->mWorkerThread == nullptr)
  290. {
  291. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  292. cm_delete(watchInfo);
  293. CM_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
  294. }
  295. }
  296. if(mPimpl->mWorkerThread != nullptr)
  297. {
  298. try
  299. {
  300. watchInfo->startMonitor(mPimpl->mCompPortHandle);
  301. }
  302. catch (Exception* e)
  303. {
  304. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  305. cm_delete(watchInfo);
  306. throw(e);
  307. }
  308. }
  309. else
  310. {
  311. mPimpl->mFoldersToWatch.erase(mPimpl->mFoldersToWatch.end() - 1);
  312. cm_delete(watchInfo);
  313. CM_EXCEPT(InternalErrorException, "Failed to create a new worker thread for folder monitoring");
  314. }
  315. }
  316. void FolderMonitor::stopMonitor(const Path& folderPath)
  317. {
  318. auto findIter = std::find_if(mPimpl->mFoldersToWatch.begin(), mPimpl->mFoldersToWatch.end(),
  319. [&](const FolderWatchInfo* x) { return x->mFolderToMonitor == folderPath; });
  320. if(findIter != mPimpl->mFoldersToWatch.end())
  321. {
  322. FolderWatchInfo* watchInfo = *findIter;
  323. watchInfo->stopMonitor(mPimpl->mCompPortHandle);
  324. cm_delete(watchInfo);
  325. mPimpl->mFoldersToWatch.erase(findIter);
  326. }
  327. if(mPimpl->mFoldersToWatch.size() == 0)
  328. stopMonitorAll();
  329. }
  330. void FolderMonitor::stopMonitorAll()
  331. {
  332. for(auto& watchInfo : mPimpl->mFoldersToWatch)
  333. {
  334. watchInfo->stopMonitor(mPimpl->mCompPortHandle);
  335. {
  336. // Note: Need this mutex to ensure worker thread is done with watchInfo.
  337. // Even though we wait for a condition variable from the worker thread in stopMonitor,
  338. // that doesn't mean the worker thread is done with the condition variable
  339. // (which is stored inside watchInfo)
  340. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  341. cm_delete(watchInfo);
  342. }
  343. }
  344. mPimpl->mFoldersToWatch.clear();
  345. if(mPimpl->mWorkerThread != nullptr)
  346. {
  347. PostQueuedCompletionStatus(mPimpl->mCompPortHandle, 0, 0, nullptr);
  348. mPimpl->mWorkerThread->join();
  349. CM_THREAD_DESTROY(mPimpl->mWorkerThread);
  350. mPimpl->mWorkerThread = nullptr;
  351. }
  352. if(mPimpl->mCompPortHandle != nullptr)
  353. {
  354. CloseHandle(mPimpl->mCompPortHandle);
  355. mPimpl->mCompPortHandle = nullptr;
  356. }
  357. }
  358. void FolderMonitor::workerThreadMain()
  359. {
  360. FolderWatchInfo* watchInfo = nullptr;
  361. do
  362. {
  363. DWORD numBytes;
  364. LPOVERLAPPED overlapped;
  365. if(!GetQueuedCompletionStatus(mPimpl->mCompPortHandle, &numBytes, (PULONG_PTR) &watchInfo, &overlapped, INFINITE))
  366. {
  367. assert(false);
  368. // TODO: Folder handle was lost most likely. Not sure how to deal with that. Shutdown watch on this folder and cleanup?
  369. }
  370. if(watchInfo != nullptr)
  371. {
  372. MonitorState state;
  373. {
  374. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  375. state = watchInfo->mState;
  376. }
  377. switch(state)
  378. {
  379. case MonitorState::Starting:
  380. if(!ReadDirectoryChangesW(watchInfo->mDirHandle, watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE,
  381. watchInfo->mMonitorSubdirectories, watchInfo->mMonitorFlags, &watchInfo->mBufferSize, &watchInfo->mOverlapped, nullptr))
  382. {
  383. assert(false); // TODO - Possibly the buffer was too small?
  384. watchInfo->mReadError = GetLastError();
  385. }
  386. else
  387. {
  388. watchInfo->mReadError = ERROR_SUCCESS;
  389. {
  390. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  391. watchInfo->mState = MonitorState::Monitoring;
  392. }
  393. }
  394. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  395. break;
  396. case MonitorState::Monitoring:
  397. {
  398. FileNotifyInfo info(watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE);
  399. handleNotifications(info, *watchInfo);
  400. if(!ReadDirectoryChangesW(watchInfo->mDirHandle, watchInfo->mBuffer, FolderWatchInfo::READ_BUFFER_SIZE,
  401. watchInfo->mMonitorSubdirectories, watchInfo->mMonitorFlags, &watchInfo->mBufferSize, &watchInfo->mOverlapped, nullptr))
  402. {
  403. assert(false); // TODO: Failed during normal operation, possibly the buffer was too small. Shutdown watch on this folder and cleanup?
  404. watchInfo->mReadError = GetLastError();
  405. }
  406. else
  407. {
  408. watchInfo->mReadError = ERROR_SUCCESS;
  409. }
  410. }
  411. break;
  412. case MonitorState::Shutdown:
  413. if(watchInfo->mDirHandle != INVALID_HANDLE_VALUE)
  414. {
  415. CloseHandle(watchInfo->mDirHandle);
  416. watchInfo->mDirHandle = INVALID_HANDLE_VALUE;
  417. {
  418. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  419. watchInfo->mState = MonitorState::Shutdown2;
  420. }
  421. }
  422. else
  423. {
  424. {
  425. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  426. watchInfo->mState = MonitorState::Inactive;
  427. }
  428. {
  429. CM_LOCK_MUTEX(mPimpl->mMainMutex); // Ensures that we don't delete "watchInfo" before this thread is done with mStartStopEvent
  430. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  431. }
  432. }
  433. break;
  434. case MonitorState::Shutdown2:
  435. if(watchInfo->mDirHandle != INVALID_HANDLE_VALUE)
  436. {
  437. // Handle is still open? Try again.
  438. CloseHandle(watchInfo->mDirHandle);
  439. watchInfo->mDirHandle = INVALID_HANDLE_VALUE;
  440. }
  441. else
  442. {
  443. {
  444. CM_LOCK_MUTEX(watchInfo->mStatusMutex);
  445. watchInfo->mState = MonitorState::Inactive;
  446. }
  447. {
  448. CM_LOCK_MUTEX(mPimpl->mMainMutex); // Ensures that we don't delete "watchInfo" before this thread is done with mStartStopEvent
  449. CM_THREAD_NOTIFY_ONE(watchInfo->mStartStopEvent);
  450. }
  451. }
  452. break;
  453. }
  454. }
  455. } while (watchInfo != nullptr);
  456. }
  457. void FolderMonitor::handleNotifications(FileNotifyInfo& notifyInfo, FolderWatchInfo& watchInfo)
  458. {
  459. Vector<FileAction*>::type mActions;
  460. do
  461. {
  462. switch(notifyInfo.getAction())
  463. {
  464. case FILE_ACTION_ADDED:
  465. {
  466. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  467. mActions.push_back(FileAction::createAdded(fileName));
  468. }
  469. break;
  470. case FILE_ACTION_REMOVED:
  471. {
  472. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  473. mActions.push_back(FileAction::createRemoved(fileName));
  474. }
  475. break;
  476. case FILE_ACTION_MODIFIED:
  477. {
  478. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  479. mActions.push_back(FileAction::createModified(fileName));
  480. }
  481. break;
  482. case FILE_ACTION_RENAMED_OLD_NAME:
  483. {
  484. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  485. watchInfo.mCachedOldFileName = fileName;
  486. }
  487. break;
  488. case FILE_ACTION_RENAMED_NEW_NAME:
  489. {
  490. WString fileName = notifyInfo.getFileNameWithPath(watchInfo.mFolderToMonitor);
  491. mActions.push_back(FileAction::createRenamed(watchInfo.mCachedOldFileName, fileName));
  492. }
  493. break;
  494. }
  495. } while(notifyInfo.getNext());
  496. {
  497. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  498. for(auto& action : mActions)
  499. mPimpl->mFileActions.push(action);
  500. }
  501. }
  502. void FolderMonitor::_update()
  503. {
  504. {
  505. CM_LOCK_MUTEX(mPimpl->mMainMutex);
  506. mPimpl->mActiveFileActions.swap(mPimpl->mFileActions);
  507. }
  508. while(!mPimpl->mActiveFileActions.empty())
  509. {
  510. FileAction* action = mPimpl->mActiveFileActions.front();
  511. mPimpl->mActiveFileActions.pop();
  512. switch(action->type)
  513. {
  514. case FileActionType::Added:
  515. if(!onAdded.empty())
  516. onAdded(Path(action->newName));
  517. break;
  518. case FileActionType::Removed:
  519. if(!onRemoved.empty())
  520. onRemoved(Path(action->newName));
  521. break;
  522. case FileActionType::Modified:
  523. if(!onModified.empty())
  524. onModified(Path(action->newName));
  525. break;
  526. case FileActionType::Renamed:
  527. if(!onRenamed.empty())
  528. onRenamed(Path(action->oldName), Path(action->newName));
  529. break;
  530. }
  531. FileAction::destroy(action);
  532. }
  533. }
  534. }