| @@ -27,6 +27,8 @@ | |||
| #include "jucer_ModuleDescription.h" | |||
| #include <future> | |||
| //============================================================================== | |||
| class AvailableModulesList : private AsyncUpdater | |||
| { | |||
| @@ -39,16 +41,36 @@ public: | |||
| //============================================================================== | |||
| 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) | |||
| { | |||
| 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: | |||
| //============================================================================== | |||
| 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 | |||
| @@ -181,31 +168,8 @@ private: | |||
| 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; | |||
| ListenerList<Listener> listeners; | |||
| CriticalSection lock; | |||