| @@ -27,6 +27,8 @@ | |||||
| #include "jucer_ModuleDescription.h" | #include "jucer_ModuleDescription.h" | ||||
| #include <future> | |||||
| //============================================================================== | //============================================================================== | ||||
| class AvailableModulesList : private AsyncUpdater | class AvailableModulesList : private AsyncUpdater | ||||
| { | { | ||||
| @@ -39,16 +41,36 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| void scanPaths (const Array<File>& paths) | void scanPaths (const Array<File>& paths) | ||||
| { | { | ||||
| auto job = createScannerJob (paths); | |||||
| auto& ref = *job; | |||||
| removePendingAndAddJob (std::move (job)); | |||||
| scanPool.waitForJobToFinish (&ref, -1); | |||||
| scanPathsAsync (paths); | |||||
| scanner = {}; | |||||
| } | } | ||||
| void scanPathsAsync (const Array<File>& paths) | void scanPathsAsync (const Array<File>& paths) | ||||
| { | { | ||||
| removePendingAndAddJob (createScannerJob (paths)); | |||||
| scanner = std::async (std::launch::async, [this, paths] | |||||
| { | |||||
| ModuleIDAndFolderList list; | |||||
| for (auto& p : paths) | |||||
| addAllModulesInFolder (p, list); | |||||
| std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1, | |||||
| const ModuleIDAndFolder& m2) | |||||
| { | |||||
| return m1.first.compareIgnoreCase (m2.first) < 0; | |||||
| }); | |||||
| { | |||||
| const ScopedLock swapLock (lock); | |||||
| if (list == modulesList) | |||||
| return; | |||||
| modulesList.swap (list); | |||||
| } | |||||
| triggerAsyncUpdate(); | |||||
| }); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -95,85 +117,50 @@ public: | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| struct ModuleScannerJob : public ThreadPoolJob | |||||
| static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list) | |||||
| { | { | ||||
| ModuleScannerJob (const Array<File>& paths, | |||||
| std::function<void (const ModuleIDAndFolderList&)>&& callback) | |||||
| : ThreadPoolJob ("ModuleScannerJob"), | |||||
| pathsToScan (paths), | |||||
| completionCallback (std::move (callback)) | |||||
| { | |||||
| } | |||||
| ModuleDescription m (path); | |||||
| JobStatus runJob() override | |||||
| if (m.isValid() | |||||
| && std::none_of (list.begin(), list.end(), | |||||
| [&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); })) | |||||
| { | { | ||||
| ModuleIDAndFolderList list; | |||||
| for (auto& p : pathsToScan) | |||||
| addAllModulesInFolder (p, list); | |||||
| if (! shouldExit()) | |||||
| { | |||||
| std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1, | |||||
| const ModuleIDAndFolder& m2) | |||||
| { | |||||
| return m1.first.compareIgnoreCase (m2.first) < 0; | |||||
| }); | |||||
| completionCallback (list); | |||||
| } | |||||
| return jobHasFinished; | |||||
| list.push_back ({ m.getID(), path }); | |||||
| return true; | |||||
| } | } | ||||
| static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list) | |||||
| { | |||||
| ModuleDescription m (path); | |||||
| return false; | |||||
| } | |||||
| if (m.isValid() | |||||
| && std::none_of (list.begin(), list.end(), | |||||
| [&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); })) | |||||
| { | |||||
| list.push_back ({ m.getID(), path }); | |||||
| return true; | |||||
| } | |||||
| static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list) | |||||
| { | |||||
| struct FileAndDepth | |||||
| { | |||||
| File file; | |||||
| int depth; | |||||
| }; | |||||
| return false; | |||||
| } | |||||
| std::queue<FileAndDepth> pathsToCheck; | |||||
| pathsToCheck.push ({ topLevelPath, 0 }); | |||||
| static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list) | |||||
| while (! pathsToCheck.empty()) | |||||
| { | { | ||||
| struct FileAndDepth | |||||
| { | |||||
| File file; | |||||
| int depth; | |||||
| }; | |||||
| const auto path = pathsToCheck.front(); | |||||
| pathsToCheck.pop(); | |||||
| std::queue<FileAndDepth> pathsToCheck; | |||||
| pathsToCheck.push ({ topLevelPath, 0 }); | |||||
| if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3) | |||||
| continue; | |||||
| while (! pathsToCheck.empty()) | |||||
| for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories)) | |||||
| { | { | ||||
| const auto path = pathsToCheck.front(); | |||||
| pathsToCheck.pop(); | |||||
| if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3) | |||||
| continue; | |||||
| if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||||
| if (job->shouldExit()) | |||||
| return; | |||||
| for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories)) | |||||
| { | |||||
| if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||||
| if (job->shouldExit()) | |||||
| return; | |||||
| pathsToCheck.push({ iter.getFile(), path.depth + 1 }); | |||||
| } | |||||
| pathsToCheck.push({ iter.getFile(), path.depth + 1 }); | |||||
| } | } | ||||
| } | } | ||||
| Array<File> pathsToScan; | |||||
| std::function<void (const ModuleIDAndFolderList&)> completionCallback; | |||||
| }; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void handleAsyncUpdate() override | void handleAsyncUpdate() override | ||||
| @@ -181,31 +168,8 @@ private: | |||||
| listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); }); | listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); }); | ||||
| } | } | ||||
| std::unique_ptr<ThreadPoolJob> createScannerJob (const Array<File>& paths) | |||||
| { | |||||
| return std::make_unique<ModuleScannerJob> (paths, [this] (ModuleIDAndFolderList scannedModulesList) | |||||
| { | |||||
| if (scannedModulesList == modulesList) | |||||
| return; | |||||
| { | |||||
| const ScopedLock swapLock (lock); | |||||
| modulesList.swap (scannedModulesList); | |||||
| } | |||||
| triggerAsyncUpdate(); | |||||
| }); | |||||
| } | |||||
| void removePendingAndAddJob (std::unique_ptr<ThreadPoolJob> jobToAdd) | |||||
| { | |||||
| scanPool.removeAllJobs (false, 100); | |||||
| scanPool.addJob (jobToAdd.release(), true); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| ThreadPool scanPool { 1 }; | |||||
| std::future<void> scanner; | |||||
| ModuleIDAndFolderList modulesList; | ModuleIDAndFolderList modulesList; | ||||
| ListenerList<Listener> listeners; | ListenerList<Listener> listeners; | ||||
| CriticalSection lock; | CriticalSection lock; | ||||