1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-31 03:00:05 +00:00

Plugin scanning: ability to use multiple threads

This commit is contained in:
jules 2013-05-03 21:21:15 +01:00
parent cc3efa2c82
commit 6810038dea
6 changed files with 93 additions and 54 deletions

View file

@ -126,6 +126,8 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
OwnedArray <PluginDescription>& typesFound,
AudioPluginFormat& format)
{
const ScopedLock sl (scanLock);
if (dontRescanIfAlreadyInList
&& getTypeForFile (fileOrIdentifier) != nullptr)
{
@ -153,14 +155,17 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
OwnedArray <PluginDescription> found;
if (scanner != nullptr)
{
if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier))
addToBlacklist (fileOrIdentifier);
}
else
{
format.findAllTypesForFile (found, fileOrIdentifier);
const ScopedUnlock sl (scanLock);
if (scanner != nullptr)
{
if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier))
addToBlacklist (fileOrIdentifier);
}
else
{
format.findAllTypesForFile (found, fileOrIdentifier);
}
}
for (int i = 0; i < found.size(); ++i)

View file

@ -201,6 +201,7 @@ private:
OwnedArray <PluginDescription> types;
StringArray blacklist;
ScopedPointer<CustomScanner> scanner;
CriticalSection scanLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList)
};

View file

@ -39,7 +39,6 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo,
: list (listToAddTo),
format (formatToLookFor),
deadMansPedalFile (deadMansPedal),
nextIndex (0),
progress (0)
{
directoriesToSearch.removeRedundantPaths();
@ -60,6 +59,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo,
}
applyBlacklistingsFromDeadMansPedal (listToAddTo, deadMansPedalFile);
nextIndex.set (filesOrIdentifiersToScan.size());
}
PluginDirectoryScanner::~PluginDirectoryScanner()
@ -69,43 +69,51 @@ PluginDirectoryScanner::~PluginDirectoryScanner()
//==============================================================================
String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const
{
return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex]);
return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex.get() - 1]);
}
void PluginDirectoryScanner::updateProgress()
{
progress = (1.0f - nextIndex.get() / (float) filesOrIdentifiersToScan.size());
}
bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList)
{
String file (filesOrIdentifiersToScan [nextIndex]);
const int index = --nextIndex;
if (file.isNotEmpty() && ! list.isListingUpToDate (file))
if (index >= 0)
{
OwnedArray <PluginDescription> typesFound;
String file (filesOrIdentifiersToScan [index]);
// Add this plugin to the end of the dead-man's pedal list in case it crashes...
StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile));
crashedPlugins.removeString (file);
crashedPlugins.add (file);
setDeadMansPedalFile (crashedPlugins);
if (file.isNotEmpty() && ! list.isListingUpToDate (file))
{
OwnedArray <PluginDescription> typesFound;
list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format);
// Add this plugin to the end of the dead-man's pedal list in case it crashes...
StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile));
crashedPlugins.removeString (file);
crashedPlugins.add (file);
setDeadMansPedalFile (crashedPlugins);
// Managed to load without crashing, so remove it from the dead-man's-pedal..
crashedPlugins.removeString (file);
setDeadMansPedalFile (crashedPlugins);
list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format);
if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file))
failedFiles.add (file);
// Managed to load without crashing, so remove it from the dead-man's-pedal..
crashedPlugins.removeString (file);
setDeadMansPedalFile (crashedPlugins);
if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file))
failedFiles.add (file);
}
}
return skipNextFile();
updateProgress();
return index > 0;
}
bool PluginDirectoryScanner::skipNextFile()
{
if (nextIndex >= filesOrIdentifiersToScan.size())
return false;
progress = ++nextIndex / (float) filesOrIdentifiersToScan.size();
return nextIndex < filesOrIdentifiersToScan.size();
updateProgress();
return --nextIndex > 0;
}
void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents)

View file

@ -115,9 +115,10 @@ private:
StringArray filesOrIdentifiersToScan;
File deadMansPedalFile;
StringArray failedFiles;
int nextIndex;
Atomic<int> nextIndex;
float progress;
void updateProgress();
void setDeadMansPedalFile (const StringArray& newContents);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner)

View file

@ -32,7 +32,7 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager,
deadMansPedalFile (deadMansPedal),
optionsButton ("Options..."),
propertiesToUse (properties),
scanOnBackgroundThread (false)
numThreads (0)
{
listBox.setModel (this);
addAndMakeVisible (&listBox);
@ -60,9 +60,9 @@ void PluginListComponent::setOptionsButtonText (const String& newText)
resized();
}
void PluginListComponent::setScansOnMessageThread (bool useMessageThread) noexcept
void PluginListComponent::setNumberOfThreadsForScanning (int num)
{
scanOnBackgroundThread = ! useMessageThread;
numThreads = num;
}
void PluginListComponent::resized()
@ -244,20 +244,18 @@ void PluginListComponent::filesDropped (const StringArray& files, int, int)
}
//==============================================================================
class PluginListComponent::Scanner : private Timer,
private Thread
class PluginListComponent::Scanner : private Timer
{
public:
Scanner (PluginListComponent& plc,
AudioPluginFormat& format,
PropertiesFile* properties,
bool useThread)
: Thread ("plugin_scan"),
owner (plc), formatToScan (format), propertiesToUse (properties),
int threads)
: owner (plc), formatToScan (format), propertiesToUse (properties),
pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon),
progressWindow (TRANS("Scanning for plug-ins..."),
TRANS("Searching for all possible plug-in files..."), AlertWindow::NoIcon),
progress (0.0), shouldUseThread (useThread), finished (false)
progress (0.0), numThreads (threads), finished (false)
{
FileSearchPath path (formatToScan.getDefaultLocationsToSearch());
@ -285,7 +283,11 @@ public:
~Scanner()
{
stopThread (10000);
if (pool != nullptr)
{
pool->removeAllJobs (true, 60000);
pool = nullptr;
}
}
private:
@ -317,8 +319,13 @@ private:
progressWindow.addProgressBarComponent (progress);
progressWindow.enterModalState();
if (shouldUseThread)
startThread();
if (numThreads > 0)
{
pool = new ThreadPool (numThreads);
for (int i = numThreads; --i >= 0;)
pool->addJob (new ScanJob (*this), true);
}
startTimer (20);
}
@ -331,7 +338,7 @@ private:
void timerCallback()
{
if (! isThreadRunning())
if (pool == nullptr)
{
if (doNextScan())
startTimer (20);
@ -346,12 +353,6 @@ private:
progressWindow.setMessage (progressMessage);
}
void run()
{
while (doNextScan() && ! threadShouldExit())
{}
}
bool doNextScan()
{
progressMessage = TRANS("Testing:\n\n") + scanner->getNextPluginFileThatWillBeScanned();
@ -374,15 +375,36 @@ private:
FileSearchPathListComponent pathList;
String progressMessage;
double progress;
bool shouldUseThread, finished;
int numThreads;
bool finished;
ScopedPointer<ThreadPool> pool;
struct ScanJob : public ThreadPoolJob
{
ScanJob (Scanner& s) : ThreadPoolJob ("pluginscan"), scanner (s) {}
JobStatus runJob()
{
while (scanner.doNextScan() && ! shouldExit())
{}
return jobHasFinished;
}
Scanner& scanner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScanJob)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scanner)
};
void PluginListComponent::scanFor (AudioPluginFormat* format)
{
if (format != nullptr)
currentScanner = new Scanner (*this, *format, propertiesToUse, scanOnBackgroundThread);
currentScanner = new Scanner (*this, *format, propertiesToUse, numThreads);
}
void PluginListComponent::scanFinished (const StringArray& failedFiles)

View file

@ -61,8 +61,10 @@ public:
/** Changes the text in the panel's button. */
void setOptionsButtonText (const String& newText);
/** Chooses whether to use the message thread or a background thread for scanning. */
void setScansOnMessageThread (bool useMessageThread) noexcept;
/** Sets how many threads to simultaneously scan for plugins.
If this is 0, then all scanning happens on the message thread (this is the default)
*/
void setNumberOfThreadsForScanning (int numThreads);
//==============================================================================
/** @internal */
@ -86,7 +88,7 @@ private:
ListBox listBox;
TextButton optionsButton;
PropertiesFile* propertiesToUse;
bool scanOnBackgroundThread;
int numThreads;
class Scanner;
friend class Scanner;