Browse Source

Plugin Scanning: Fix thread sanitizer issues in the AudioPluginHost

v6.1.6
reuk 3 years ago
parent
commit
093dbc7df1
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
5 changed files with 109 additions and 48 deletions
  1. +1
    -1
      modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h
  2. +4
    -4
      modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp
  3. +19
    -2
      modules/juce_core/files/juce_TemporaryFile.cpp
  4. +75
    -37
      modules/juce_core/native/juce_posix_NamedPipe.cpp
  5. +10
    -4
      modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp

+ 1
- 1
modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h View File

@@ -126,7 +126,7 @@ private:
File deadMansPedalFile; File deadMansPedalFile;
StringArray failedFiles; StringArray failedFiles;
Atomic<int> nextIndex; Atomic<int> nextIndex;
float progress = 0;
std::atomic<float> progress { 0.0f };
const bool allowAsync; const bool allowAsync;
void updateProgress(); void updateProgress();


+ 4
- 4
modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp View File

@@ -451,7 +451,8 @@ private:
String pluginBeingScanned; String pluginBeingScanned;
double progress = 0; double progress = 0;
const int numThreads; const int numThreads;
bool allowAsync, finished = false, timerReentrancyCheck = false;
bool allowAsync, timerReentrancyCheck = false;
std::atomic<bool> finished { false };
std::unique_ptr<ThreadPool> pool; std::unique_ptr<ThreadPool> pool;
std::set<String> initiallyBlacklistedFiles; std::set<String> initiallyBlacklistedFiles;
@@ -582,6 +583,8 @@ private:
if (timerReentrancyCheck) if (timerReentrancyCheck)
return; return;
progress = scanner->getProgress();
if (pool == nullptr) if (pool == nullptr)
{ {
const ScopedValueSetter<bool> setter (timerReentrancyCheck, true); const ScopedValueSetter<bool> setter (timerReentrancyCheck, true);
@@ -602,10 +605,7 @@ private:
bool doNextScan() bool doNextScan()
{ {
if (scanner->scanNextFile (true, pluginBeingScanned)) if (scanner->scanNextFile (true, pluginBeingScanned))
{
progress = scanner->getProgress();
return true; return true;
}
finished = true; finished = true;
return false; return false;


+ 19
- 2
modules/juce_core/files/juce_TemporaryFile.cpp View File

@@ -23,6 +23,23 @@
namespace juce namespace juce
{ {
// Using Random::getSystemRandom() can be a bit dangerous in multithreaded contexts!
class LockedRandom
{
public:
int nextInt()
{
const ScopedLock lock (mutex);
return random.nextInt();
}
private:
CriticalSection mutex;
Random random;
};
static LockedRandom lockedRandom;
static File createTempFile (const File& parentDirectory, String name, static File createTempFile (const File& parentDirectory, String name,
const String& suffix, int optionFlags) const String& suffix, int optionFlags)
{ {
@@ -34,7 +51,7 @@ static File createTempFile (const File& parentDirectory, String name,
TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags)
: temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), : temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory),
"temp_" + String::toHexString (Random::getSystemRandom().nextInt()),
"temp_" + String::toHexString (lockedRandom.nextInt()),
suffix, optionFlags)), suffix, optionFlags)),
targetFile() targetFile()
{ {
@@ -43,7 +60,7 @@ TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags)
TemporaryFile::TemporaryFile (const File& target, const int optionFlags) TemporaryFile::TemporaryFile (const File& target, const int optionFlags)
: temporaryFile (createTempFile (target.getParentDirectory(), : temporaryFile (createTempFile (target.getParentDirectory(),
target.getFileNameWithoutExtension() target.getFileNameWithoutExtension()
+ "_temp" + String::toHexString (Random::getSystemRandom().nextInt()),
+ "_temp" + String::toHexString (lockedRandom.nextInt()),
target.getFileExtension(), optionFlags)), target.getFileExtension(), optionFlags)),
targetFile (target) targetFile (target)
{ {


+ 75
- 37
modules/juce_core/native/juce_posix_NamedPipe.cpp View File

@@ -39,8 +39,8 @@ public:
~Pimpl() ~Pimpl()
{ {
if (pipeIn != -1) ::close (pipeIn);
if (pipeOut != -1) ::close (pipeOut);
pipeIn .close();
pipeOut.close();
if (createdPipe) if (createdPipe)
{ {
@@ -51,7 +51,7 @@ public:
bool connect (int timeOutMilliseconds) bool connect (int timeOutMilliseconds)
{ {
return openPipe (true, getTimeoutEnd (timeOutMilliseconds));
return openPipe (true, getTimeoutEnd (timeOutMilliseconds)) != invalidPipe;
} }
int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
@@ -61,8 +61,10 @@ public:
while (bytesRead < maxBytesToRead) while (bytesRead < maxBytesToRead)
{ {
const auto pipe = pipeIn.get();
auto bytesThisTime = maxBytesToRead - bytesRead; auto bytesThisTime = maxBytesToRead - bytesRead;
auto numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime);
auto numRead = (int) ::read (pipe, destBuffer, (size_t) bytesThisTime);
if (numRead <= 0) if (numRead <= 0)
{ {
@@ -72,9 +74,9 @@ public:
return -1; return -1;
const int maxWaitingTime = 30; const int maxWaitingTime = 30;
waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime
: jmin (maxWaitingTime,
(int) (timeoutEnd - Time::getMillisecondCounter())));
waitForInput (pipe, timeoutEnd == 0 ? maxWaitingTime
: jmin (maxWaitingTime,
(int) (timeoutEnd - Time::getMillisecondCounter())));
continue; continue;
} }
@@ -89,7 +91,9 @@ public:
{ {
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds); auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
if (! openPipe (false, timeoutEnd))
const auto pipe = openPipe (false, timeoutEnd);
if (pipe == invalidPipe)
return -1; return -1;
int bytesWritten = 0; int bytesWritten = 0;
@@ -97,7 +101,7 @@ public:
while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd))
{ {
auto bytesThisTime = numBytesToWrite - bytesWritten; auto bytesThisTime = numBytesToWrite - bytesWritten;
auto numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime);
auto numWritten = (int) ::write (pipe, sourceBuffer, (size_t) bytesThisTime);
if (numWritten < 0) if (numWritten < 0)
{ {
@@ -105,9 +109,9 @@ public:
const int maxWaitingTime = 30; const int maxWaitingTime = 30;
if (error == EWOULDBLOCK || error == EAGAIN) if (error == EWOULDBLOCK || error == EAGAIN)
waitToWrite (pipeOut, timeoutEnd == 0 ? maxWaitingTime
: jmin (maxWaitingTime,
(int) (timeoutEnd - Time::getMillisecondCounter())));
waitToWrite (pipe, timeoutEnd == 0 ? maxWaitingTime
: jmin (maxWaitingTime,
(int) (timeoutEnd - Time::getMillisecondCounter())));
else else
return -1; return -1;
@@ -134,8 +138,52 @@ public:
return createdFifoIn && createdFifoOut; return createdFifoIn && createdFifoOut;
} }
static constexpr auto invalidPipe = -1;
class PipeDescriptor
{
public:
template <typename Fn>
int get (Fn&& fn)
{
{
const ScopedReadLock l (mutex);
if (descriptor != invalidPipe)
return descriptor;
}
const ScopedWriteLock l (mutex);
return descriptor = fn();
}
void close()
{
{
const ScopedReadLock l (mutex);
if (descriptor == invalidPipe)
return;
}
const ScopedWriteLock l (mutex);
::close (descriptor);
descriptor = invalidPipe;
}
int get()
{
const ScopedReadLock l (mutex);
return descriptor;
}
private:
ReadWriteLock mutex;
int descriptor = invalidPipe;
};
const String pipeInName, pipeOutName; const String pipeInName, pipeOutName;
int pipeIn = -1, pipeOut = -1;
PipeDescriptor pipeIn, pipeOut;
bool createdFifoIn = false, createdFifoOut = false; bool createdFifoIn = false, createdFifoOut = false;
const bool createdPipe; const bool createdPipe;
@@ -160,30 +208,25 @@ private:
{ {
auto p = ::open (name.toUTF8(), flags); auto p = ::open (name.toUTF8(), flags);
if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation.load())
if (p != invalidPipe || hasExpired (timeoutEnd) || stopReadOperation.load())
return p; return p;
Thread::sleep (2); Thread::sleep (2);
} }
} }
bool openPipe (bool isInput, uint32 timeoutEnd)
int openPipe (bool isInput, uint32 timeoutEnd)
{ {
auto& pipe = isInput ? pipeIn : pipeOut; auto& pipe = isInput ? pipeIn : pipeOut;
int flags = (isInput ? O_RDWR : O_WRONLY) | O_NONBLOCK;
const auto flags = (isInput ? O_RDWR : O_WRONLY) | O_NONBLOCK;
const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName) const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName)
: (createdPipe ? pipeOutName : pipeInName); : (createdPipe ? pipeOutName : pipeInName);
if (pipe == -1)
return pipe.get ([this, &pipeName, &flags, &timeoutEnd]
{ {
pipe = openPipe (pipeName, flags, timeoutEnd);
if (pipe == -1)
return false;
}
return true;
return openPipe (pipeName, flags, timeoutEnd);
});
} }
static void waitForInput (int handle, int timeoutMsecs) noexcept static void waitForInput (int handle, int timeoutMsecs) noexcept
@@ -203,23 +246,18 @@ private:
void NamedPipe::close() void NamedPipe::close()
{ {
{
ScopedReadLock sl (lock);
ScopedWriteLock sl (lock);
if (pimpl != nullptr)
{
pimpl->stopReadOperation = true;
if (pimpl != nullptr)
{
pimpl->stopReadOperation = true;
char buffer[1] = { 0 };
ssize_t done = ::write (pimpl->pipeIn, buffer, 1);
ignoreUnused (done);
}
const char buffer[] { 0 };
const auto done = ::write (pimpl->pipeIn.get(), buffer, numElementsInArray (buffer));
ignoreUnused (done);
} }
{
ScopedWriteLock sl (lock);
pimpl.reset();
}
pimpl.reset();
} }
bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool mustNotExist) bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool mustNotExist)


+ 10
- 4
modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp View File

@@ -51,6 +51,8 @@ struct ChildProcessPingThread : public Thread,
pingReceived(); pingReceived();
} }
void startPinging() { startThread (4); }
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
void triggerConnectionLostMessage() { triggerAsyncUpdate(); } void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
@@ -90,8 +92,7 @@ struct ChildProcessCoordinator::Connection : public InterprocessConnection,
ChildProcessPingThread (timeout), ChildProcessPingThread (timeout),
owner (m) owner (m)
{ {
if (createPipe (pipeName, timeoutMs))
startThread (4);
createPipe (pipeName, timeoutMs);
} }
~Connection() override ~Connection() override
@@ -99,6 +100,8 @@ struct ChildProcessCoordinator::Connection : public InterprocessConnection,
stopThread (10000); stopThread (10000);
} }
using ChildProcessPingThread::startPinging;
private: private:
void connectionMade() override {} void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); } void connectionLost() override { owner.handleConnectionLost(); }
@@ -198,7 +201,6 @@ struct ChildProcessWorker::Connection : public InterprocessConnection,
owner (p) owner (p)
{ {
connectToPipe (pipeName, timeoutMs); connectToPipe (pipeName, timeoutMs);
startThread (4);
} }
~Connection() override ~Connection() override
@@ -207,6 +209,8 @@ struct ChildProcessWorker::Connection : public InterprocessConnection,
disconnect(); disconnect();
} }
using ChildProcessPingThread::startPinging;
private: private:
ChildProcessWorker& owner; ChildProcessWorker& owner;
@@ -275,7 +279,9 @@ bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine,
{ {
connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs)); connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
if (! connection->isConnected())
if (connection->isConnected())
connection->startPinging();
else
connection.reset(); connection.reset();
} }
} }


Loading…
Cancel
Save