#include "CustomPluginScanner.hpp" #include "common.h" using namespace juce; class ScannerSuperprocess final : private ChildProcessCoordinator { public: ScannerSuperprocess(const String& architecture) { launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile), scannerProcessUID, 0, 0, architecture); } enum class State { timeout, gotResult, connectionLost, }; struct Response { State state; std::unique_ptr xml; }; Response getResponse() { std::unique_lock lock { mutex }; if (! condvar.wait_for (lock, std::chrono::milliseconds { 50 }, [&] { return gotResult || connectionLost; })) { return { State::timeout, nullptr }; } const auto state = connectionLost ? State::connectionLost : State::gotResult; connectionLost = false; gotResult = false; return { state, std::move (pluginDescription) }; } using ChildProcessCoordinator::sendMessageToWorker; private: void handleMessageFromWorker (const MemoryBlock& mb) override { const std::lock_guard lock { mutex }; pluginDescription = parseXML (mb.toString()); gotResult = true; condvar.notify_one(); } void handleConnectionLost() override { const std::lock_guard lock { mutex }; connectionLost = true; condvar.notify_one(); } std::mutex mutex; std::condition_variable condvar; std::unique_ptr pluginDescription; bool connectionLost = false; bool gotResult = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScannerSuperprocess) }; CustomPluginScanner::CustomPluginScanner(const String& architecture, const std::list & skipIdentifiers) : architecture(architecture), skipIdentifiers(skipIdentifiers.cbegin(), skipIdentifiers.cend()) {} CustomPluginScanner::~CustomPluginScanner() {} bool CustomPluginScanner::findPluginTypesFor (AudioPluginFormat& format, OwnedArray& result, const String& fileOrIdentifier) { if (addPluginDescriptions (format.getName(), fileOrIdentifier, result)) { return true; } std::unique_lock lock(threadSuperprocessMutex); threadSuperprocess[platformGetCurrentThread()] = nullptr; return false; } void CustomPluginScanner::scanFinished() { std::unique_lock lock(threadSuperprocessMutex); threadSuperprocess[platformGetCurrentThread()] = nullptr; } bool CustomPluginScanner::addPluginDescriptions (const String& formatName, const String& fileOrIdentifier, OwnedArray& result) { if (std::find(skipIdentifiers.cbegin(), skipIdentifiers.cend(), fileOrIdentifier) != skipIdentifiers.end()) { std::cerr << "Skipping " << fileOrIdentifier << "\n"; return true; } ScannerSuperprocess * superprocess = nullptr; { std::unique_lock lock(threadSuperprocessMutex); auto it = threadSuperprocess.find(platformGetCurrentThread()); if(it == threadSuperprocess.end() || !threadSuperprocess[platformGetCurrentThread()]) { threadSuperprocess[platformGetCurrentThread()] = std::make_unique(architecture); superprocess = threadSuperprocess[platformGetCurrentThread()].get(); } superprocess = threadSuperprocess[platformGetCurrentThread()].get(); } MemoryBlock block; MemoryOutputStream stream { block, true }; stream.writeString (formatName); stream.writeString (fileOrIdentifier); if (! superprocess->sendMessageToWorker (block)) return false; using clock_t = std::chrono::high_resolution_clock; auto startTime = clock_t::now(); for (;;) { if (shouldExit()) return true; const auto response = superprocess->getResponse(); auto elapsedTime = clock_t::now() - startTime; if(elapsedTime > std::chrono::seconds(10)) { return false; } if (response.state == ScannerSuperprocess::State::timeout) continue; if (response.xml != nullptr) { for (const auto* item : response.xml->getChildIterator()) { auto desc = std::make_unique(); if (desc->loadFromXml (*item)) result.add (std::move (desc)); } } return (response.state == ScannerSuperprocess::State::gotResult); } }