Loading...
Searching...
No Matches
juce_ConnectedChildProcess.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
enum { magicCoordWorkerConnectionHeader = 0x712baf04 };
static const char* startMessage = "__ipc_st";
static const char* killMessage = "__ipc_k_";
static const char* pingMessage = "__ipc_p_";
enum { specialMessageSize = 8, defaultTimeoutMs = 8000 };
static bool isMessageType (const MemoryBlock& mb, const char* messageType) noexcept
{
return mb.matches (messageType, (size_t) specialMessageSize);
}
static String getCommandLinePrefix (const String& commandLineUniqueID)
{
return "--" + commandLineUniqueID + ":";
}
//==============================================================================
// This thread sends and receives ping messages every second, so that it
// can find out if the other process has stopped running.
struct ChildProcessPingThread : public Thread,
private AsyncUpdater
{
ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout)
{
pingReceived();
}
void startPinging() { startThread (Priority::low); }
void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
virtual bool sendPingMessage (const MemoryBlock&) = 0;
virtual void pingFailed() = 0;
int timeoutMs;
using AsyncUpdater::cancelPendingUpdate;
private:
Atomic<int> countdown;
void handleAsyncUpdate() override { pingFailed(); }
void run() override
{
while (! threadShouldExit())
{
if (--countdown <= 0 || ! sendPingMessage ({ pingMessage, specialMessageSize }))
{
triggerConnectionLostMessage();
break;
}
wait (1000);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread)
};
//==============================================================================
struct ChildProcessCoordinator::Connection final : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessCoordinator& m, const String& pipeName, int timeout)
: InterprocessConnection (false, magicCoordWorkerConnectionHeader),
ChildProcessPingThread (timeout),
owner (m)
{
createPipe (pipeName, timeoutMs);
}
~Connection() override
{
cancelPendingUpdate();
stopThread (10000);
}
using ChildProcessPingThread::startPinging;
private:
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToWorker (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage))
owner.handleMessageFromWorker (m);
}
ChildProcessCoordinator& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessCoordinator::ChildProcessCoordinator() = default;
ChildProcessCoordinator::~ChildProcessCoordinator()
{
killWorkerProcess();
}
void ChildProcessCoordinator::handleConnectionLost() {}
void ChildProcessCoordinator::handleMessageFromWorker (const MemoryBlock& mb)
{
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
handleMessageFromSlave (mb);
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
}
bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessCoordinator::launchWorkerProcess (const File& executable, const String& commandLineUniqueID,
int timeoutMs, int streamFlags)
{
killWorkerProcess();
auto pipeName = "p" + String::toHexString (Random().nextInt64());
StringArray args;
args.add (executable.getFullPathName());
args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName);
childProcess = [&]() -> std::shared_ptr<ChildProcess>
{
if ((SystemStats::getOperatingSystemType() & SystemStats::Linux) != 0)
return ChildProcessManager::getInstance()->createAndStartManagedChildProcess (args, streamFlags);
auto p = std::make_shared<ChildProcess>();
if (p->start (args, streamFlags))
return p;
return nullptr;
}();
if (childProcess != nullptr)
{
connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
if (connection->isConnected())
{
connection->startPinging();
sendMessageToWorker ({ startMessage, specialMessageSize });
return true;
}
connection.reset();
}
return false;
}
void ChildProcessCoordinator::killWorkerProcess()
{
if (connection != nullptr)
{
sendMessageToWorker ({ killMessage, specialMessageSize });
connection->disconnect();
connection.reset();
}
childProcess.reset();
}
//==============================================================================
struct ChildProcessWorker::Connection final : public InterprocessConnection,
private ChildProcessPingThread
{
Connection (ChildProcessWorker& p, const String& pipeName, int timeout)
: InterprocessConnection (false, magicCoordWorkerConnectionHeader),
ChildProcessPingThread (timeout),
owner (p)
{
connectToPipe (pipeName, timeoutMs);
}
~Connection() override
{
cancelPendingUpdate();
stopThread (10000);
disconnect();
}
using ChildProcessPingThread::startPinging;
private:
ChildProcessWorker& owner;
void connectionMade() override {}
void connectionLost() override { owner.handleConnectionLost(); }
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToCoordinator (m); }
void pingFailed() override { connectionLost(); }
void messageReceived (const MemoryBlock& m) override
{
pingReceived();
if (isMessageType (m, pingMessage))
return;
if (isMessageType (m, killMessage))
return triggerConnectionLostMessage();
if (isMessageType (m, startMessage))
return owner.handleConnectionMade();
owner.handleMessageFromCoordinator (m);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
};
//==============================================================================
ChildProcessWorker::ChildProcessWorker() = default;
ChildProcessWorker::~ChildProcessWorker() = default;
void ChildProcessWorker::handleConnectionMade() {}
void ChildProcessWorker::handleConnectionLost() {}
void ChildProcessWorker::handleMessageFromCoordinator (const MemoryBlock& mb)
{
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
handleMessageFromMaster (mb);
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_END_IGNORE_WARNINGS_MSVC
}
bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb)
{
if (connection != nullptr)
return connection->sendMessage (mb);
jassertfalse; // this can only be used when the connection is active!
return false;
}
bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID,
int timeoutMs)
{
auto prefix = getCommandLinePrefix (commandLineUniqueID);
if (commandLine.trim().startsWith (prefix))
{
auto pipeName = commandLine.fromFirstOccurrenceOf (prefix, false, false)
.upToFirstOccurrenceOf (" ", false, false).trim();
if (pipeName.isNotEmpty())
{
connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
if (connection->isConnected())
connection->startPinging();
else
connection.reset();
}
}
return connection != nullptr;
}
} // namespace juce
juce::AsyncUpdater
Definition juce_AsyncUpdater.h:39
juce::AsyncUpdater::handleAsyncUpdate
virtual void handleAsyncUpdate()=0
juce::AsyncUpdater::triggerAsyncUpdate
void triggerAsyncUpdate()
Definition juce_AsyncUpdater.cpp:62
juce::AsyncUpdater::cancelPendingUpdate
void cancelPendingUpdate() noexcept
Definition juce_AsyncUpdater.cpp:74
juce::ChildProcessCoordinator::killWorkerProcess
void killWorkerProcess()
Definition juce_ConnectedChildProcess.cpp:197
juce::ChildProcessCoordinator::ChildProcessCoordinator
ChildProcessCoordinator()
juce::ChildProcessCoordinator::~ChildProcessCoordinator
virtual ~ChildProcessCoordinator()
Definition juce_ConnectedChildProcess.cpp:131
juce::ChildProcessCoordinator::sendMessageToWorker
bool sendMessageToWorker(const MemoryBlock &)
Definition juce_ConnectedChildProcess.cpp:147
juce::ChildProcessCoordinator::handleMessageFromWorker
virtual void handleMessageFromWorker(const MemoryBlock &)
Definition juce_ConnectedChildProcess.cpp:138
juce::ChildProcessCoordinator::handleConnectionLost
virtual void handleConnectionLost()
Definition juce_ConnectedChildProcess.cpp:136
juce::ChildProcessCoordinator::launchWorkerProcess
bool launchWorkerProcess(const File &executableToLaunch, const String &commandLineUniqueID, int timeoutMs=0, int streamFlags=ChildProcess::wantStdOut|ChildProcess::wantStdErr)
Definition juce_ConnectedChildProcess.cpp:156
juce::ChildProcessWorker
Definition juce_ConnectedChildProcess.h:48
juce::ChildProcessWorker::sendMessageToCoordinator
bool sendMessageToCoordinator(const MemoryBlock &)
Definition juce_ConnectedChildProcess.cpp:274
juce::ChildProcessWorker::ChildProcessWorker
ChildProcessWorker()
juce::ChildProcessWorker::~ChildProcessWorker
virtual ~ChildProcessWorker()
juce::ChildProcessWorker::handleMessageFromCoordinator
virtual void handleMessageFromCoordinator(const MemoryBlock &mb)
Definition juce_ConnectedChildProcess.cpp:265
juce::ChildProcessWorker::initialiseFromCommandLine
bool initialiseFromCommandLine(const String &commandLine, const String &commandLineUniqueID, int timeoutMs=0)
Definition juce_ConnectedChildProcess.cpp:283
juce::ChildProcessWorker::handleConnectionLost
virtual void handleConnectionLost()
Definition juce_ConnectedChildProcess.cpp:263
juce::ChildProcessWorker::handleConnectionMade
virtual void handleConnectionMade()
Definition juce_ConnectedChildProcess.cpp:262
juce::File
Definition juce_File.h:45
juce::File::getFullPathName
const String & getFullPathName() const noexcept
Definition juce_File.h:153
juce::InterprocessConnection
Definition juce_InterprocessConnection.h:54
juce::InterprocessConnection::InterprocessConnection
InterprocessConnection(bool callbacksOnMessageThread=true, uint32 magicMessageHeaderNumber=0xf2b49e2c)
Definition juce_InterprocessConnection.cpp:74
juce::InterprocessConnection::createPipe
bool createPipe(const String &pipeName, int pipeReceiveMessageTimeoutMs, bool mustNotExist=false)
Definition juce_InterprocessConnection.cpp:131
juce::MemoryBlock
Definition juce_MemoryBlock.h:33
juce::Random
Definition juce_Random.h:35
juce::StringArray
Definition juce_StringArray.h:35
juce::StringArray::add
void add(String stringToAdd)
Definition juce_StringArray.cpp:136
juce::String
Definition juce_String.h:53
juce::String::upToFirstOccurrenceOf
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1585
juce::String::trim
String trim() const
Definition juce_String.cpp:1656
juce::String::startsWith
bool startsWith(StringRef text) const noexcept
Definition juce_String.cpp:1393
juce::String::toHexString
static String toHexString(IntegerType number)
Definition juce_String.h:1097
juce::String::fromFirstOccurrenceOf
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1565
juce::SystemStats::getOperatingSystemType
static OperatingSystemType getOperatingSystemType()
juce::Thread
Definition juce_Thread.h:43
juce::Thread::wait
bool wait(double timeOutMilliseconds) const
Definition juce_Thread.cpp:288
juce::Thread::Thread
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
Definition juce_Thread.cpp:26
juce::Thread::run
virtual void run()=0
juce::Thread::startThread
bool startThread()
Definition juce_Thread.cpp:145
juce::Thread::threadShouldExit
bool threadShouldExit() const
Definition juce_Thread.cpp:202
juce::Thread::Priority::low
@ low
Definition juce_Thread.h:65