@@ -97,7 +97,7 @@ public: | |||
testResultsBox.setMultiLine (true); | |||
testResultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain }); | |||
logMessage (String ("This demo uses the ChildProcessMaster and ChildProcessSlave classes to launch and communicate " | |||
logMessage (String ("This demo uses the ChildProcessCoordinator and ChildProcessWorker classes to launch and communicate " | |||
"with a child process, sending messages in the form of serialised ValueTree objects.") + newLine); | |||
setSize (500, 500); | |||
@@ -105,7 +105,7 @@ public: | |||
~ChildProcessDemo() override | |||
{ | |||
masterProcess.reset(); | |||
coordinatorProcess.reset(); | |||
} | |||
void paint (Graphics& g) override | |||
@@ -134,11 +134,11 @@ public: | |||
// invoked by the 'launch' button. | |||
void launchChildProcess() | |||
{ | |||
if (masterProcess.get() == nullptr) | |||
if (coordinatorProcess.get() == nullptr) | |||
{ | |||
masterProcess.reset (new DemoMasterProcess (*this)); | |||
coordinatorProcess.reset (new DemoCoordinatorProcess (*this)); | |||
if (masterProcess->launchSlaveProcess (File::getSpecialLocation (File::currentExecutableFile), demoCommandLineUID)) | |||
if (coordinatorProcess->launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile), demoCommandLineUID)) | |||
logMessage ("Child process started"); | |||
} | |||
} | |||
@@ -146,8 +146,8 @@ public: | |||
// invoked by the 'ping' button. | |||
void pingChildProcess() | |||
{ | |||
if (masterProcess.get() != nullptr) | |||
masterProcess->sendPingMessageToSlave(); | |||
if (coordinatorProcess.get() != nullptr) | |||
coordinatorProcess->sendPingMessageToWorker(); | |||
else | |||
logMessage ("Child process is not running!"); | |||
} | |||
@@ -155,45 +155,45 @@ public: | |||
// invoked by the 'kill' button. | |||
void killChildProcess() | |||
{ | |||
if (masterProcess.get() != nullptr) | |||
if (coordinatorProcess.get() != nullptr) | |||
{ | |||
masterProcess.reset(); | |||
coordinatorProcess.reset(); | |||
logMessage ("Child process killed"); | |||
} | |||
} | |||
//============================================================================== | |||
// This class is used by the main process, acting as the master and receiving messages | |||
// from the slave process. | |||
class DemoMasterProcess : public ChildProcessMaster, | |||
private DeletedAtShutdown | |||
// This class is used by the main process, acting as the coordinator and receiving messages | |||
// from the worker process. | |||
class DemoCoordinatorProcess : public ChildProcessCoordinator, | |||
private DeletedAtShutdown | |||
{ | |||
public: | |||
DemoMasterProcess (ChildProcessDemo& d) : demo (d) {} | |||
DemoCoordinatorProcess (ChildProcessDemo& d) : demo (d) {} | |||
// This gets called when a message arrives from the slave process.. | |||
void handleMessageFromSlave (const MemoryBlock& mb) override | |||
// This gets called when a message arrives from the worker process.. | |||
void handleMessageFromWorker (const MemoryBlock& mb) override | |||
{ | |||
auto incomingMessage = memoryBlockToValueTree (mb); | |||
demo.logMessage ("Received: " + valueTreeToString (incomingMessage)); | |||
} | |||
// This gets called if the slave process dies. | |||
// This gets called if the worker process dies. | |||
void handleConnectionLost() override | |||
{ | |||
demo.logMessage ("Connection lost to child process!"); | |||
demo.killChildProcess(); | |||
} | |||
void sendPingMessageToSlave() | |||
void sendPingMessageToWorker() | |||
{ | |||
ValueTree message ("MESSAGE"); | |||
message.setProperty ("count", count++, nullptr); | |||
demo.logMessage ("Sending: " + valueTreeToString (message)); | |||
sendMessageToSlave (valueTreeToMemoryBlock (message)); | |||
sendMessageToWorker (valueTreeToMemoryBlock (message)); | |||
} | |||
ChildProcessDemo& demo; | |||
@@ -201,7 +201,7 @@ public: | |||
}; | |||
//============================================================================== | |||
std::unique_ptr<DemoMasterProcess> masterProcess; | |||
std::unique_ptr<DemoCoordinatorProcess> coordinatorProcess; | |||
private: | |||
TextButton launchButton { "Launch Child Process" }; | |||
@@ -234,15 +234,15 @@ private: | |||
//============================================================================== | |||
/* This class gets instantiated in the child process, and receives messages from | |||
the master process. | |||
the coordinator process. | |||
*/ | |||
class DemoSlaveProcess : public ChildProcessSlave, | |||
private DeletedAtShutdown | |||
class DemoWorkerProcess : public ChildProcessWorker, | |||
private DeletedAtShutdown | |||
{ | |||
public: | |||
DemoSlaveProcess() {} | |||
DemoWorkerProcess() = default; | |||
void handleMessageFromMaster (const MemoryBlock& mb) override | |||
void handleMessageFromCoordinator (const MemoryBlock& mb) override | |||
{ | |||
ValueTree incomingMessage (memoryBlockToValueTree (mb)); | |||
@@ -256,7 +256,7 @@ public: | |||
ValueTree reply ("REPLY"); | |||
reply.setProperty ("countPlusOne", static_cast<int> (incomingMessage["count"]) + 1, nullptr); | |||
sendMessageToMaster (valueTreeToMemoryBlock (reply)); | |||
sendMessageToCoordinator (valueTreeToMemoryBlock (reply)); | |||
} | |||
void handleConnectionMade() override | |||
@@ -264,10 +264,10 @@ public: | |||
// This method is called when the connection is established, and in response, we'll just | |||
// send off a message to say hello. | |||
ValueTree reply ("HelloWorld"); | |||
sendMessageToMaster (valueTreeToMemoryBlock (reply)); | |||
sendMessageToCoordinator (valueTreeToMemoryBlock (reply)); | |||
} | |||
/* If no pings are received from the master process for a number of seconds, then this will get invoked. | |||
/* If no pings are received from the coordinator process for a number of seconds, then this will get invoked. | |||
Typically you'll want to use this as a signal to kill the process as quickly as possible, as you | |||
don't want to leave it hanging around as a zombie.. | |||
*/ | |||
@@ -284,11 +284,11 @@ public: | |||
*/ | |||
bool invokeChildProcessDemo (const String& commandLine) | |||
{ | |||
std::unique_ptr<DemoSlaveProcess> slave (new DemoSlaveProcess()); | |||
std::unique_ptr<DemoWorkerProcess> worker (new DemoWorkerProcess()); | |||
if (slave->initialiseFromCommandLine (commandLine, demoCommandLineUID)) | |||
if (worker->initialiseFromCommandLine (commandLine, demoCommandLineUID)) | |||
{ | |||
slave.release(); // allow the slave object to stay alive - it'll handle its own deletion. | |||
worker.release(); // allow the worker object to stay alive - it'll handle its own deletion. | |||
return true; | |||
} | |||
@@ -31,7 +31,7 @@ | |||
#error "If you're building the audio plugin host, you probably want to enable VST and/or AU support" | |||
#endif | |||
class PluginScannerSubprocess : private ChildProcessSlave, | |||
class PluginScannerSubprocess : private ChildProcessWorker, | |||
private AsyncUpdater | |||
{ | |||
public: | |||
@@ -40,10 +40,10 @@ public: | |||
formatManager.addDefaultFormats(); | |||
} | |||
using ChildProcessSlave::initialiseFromCommandLine; | |||
using ChildProcessWorker::initialiseFromCommandLine; | |||
private: | |||
void handleMessageFromMaster (const MemoryBlock& mb) override | |||
void handleMessageFromCoordinator (const MemoryBlock& mb) override | |||
{ | |||
{ | |||
const std::lock_guard<std::mutex> lock (mutex); | |||
@@ -94,7 +94,7 @@ private: | |||
xml.addChildElement (desc->createXml().release()); | |||
const auto str = xml.toString(); | |||
sendMessageToMaster ({ str.toRawUTF8(), str.getNumBytesAsUTF8() }); | |||
sendMessageToCoordinator ({ str.toRawUTF8(), str.getNumBytesAsUTF8() }); | |||
} | |||
} | |||
@@ -71,7 +71,7 @@ public: | |||
stream.writeString (format.getName()); | |||
stream.writeString (fileOrIdentifier); | |||
if (superprocess->sendMessageToSlave (block)) | |||
if (superprocess->sendMessageToWorker (block)) | |||
{ | |||
std::unique_lock<std::mutex> lock (mutex); | |||
gotResponse = false; | |||
@@ -123,17 +123,19 @@ public: | |||
} | |||
private: | |||
class Superprocess : public ChildProcessMaster | |||
class Superprocess : private ChildProcessCoordinator | |||
{ | |||
public: | |||
explicit Superprocess (CustomPluginScanner& o) | |||
: owner (o) | |||
{ | |||
launchSlaveProcess (File::getSpecialLocation (File::currentExecutableFile), processUID, 0, 0); | |||
launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile), processUID, 0, 0); | |||
} | |||
using ChildProcessCoordinator::sendMessageToWorker; | |||
private: | |||
void handleMessageFromSlave (const MemoryBlock& mb) override | |||
void handleMessageFromWorker (const MemoryBlock& mb) override | |||
{ | |||
auto xml = parseXML (mb.toString()); | |||
@@ -37,7 +37,7 @@ add_library( ${BINARY_NAME} | |||
"../../../Source/Demos.h" | |||
"../../../Source/Main.cpp" | |||
"../../../Source/MasterComponent.h" | |||
"../../../Source/SlaveComponent.h" | |||
"../../../Source/ClientComponent.h" | |||
"../../../Source/SharedCanvas.h" | |||
"../../../Source/juce_icon.png" | |||
"../../../../../modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h" | |||
@@ -1623,7 +1623,7 @@ add_library( ${BINARY_NAME} | |||
set_source_files_properties("../../../Source/Demos.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../Source/MasterComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../Source/SlaveComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../Source/ClientComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../Source/SharedCanvas.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../Source/juce_icon.png" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
set_source_files_properties("../../../../../modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h" PROPERTIES HEADER_FILE_ONLY TRUE) | |||
@@ -67,7 +67,6 @@ | |||
7BE6330821794919A88ED8ED /* include_juce_gui_extra.mm */ /* include_juce_gui_extra.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_extra.mm; path = ../../JuceLibraryCode/include_juce_gui_extra.mm; sourceTree = SOURCE_ROOT; }; | |||
84B287BB2AD252B7D69AC47E /* include_juce_gui_basics.mm */ /* include_juce_gui_basics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_basics.mm; path = ../../JuceLibraryCode/include_juce_gui_basics.mm; sourceTree = SOURCE_ROOT; }; | |||
89583CD42AD218E9753DF11C /* juce_audio_devices */ /* juce_audio_devices */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_devices; path = ../../../../modules/juce_audio_devices; sourceTree = SOURCE_ROOT; }; | |||
8E2F72AFA0CDA64F0C07F105 /* SlaveComponent.h */ /* SlaveComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SlaveComponent.h; path = ../../Source/SlaveComponent.h; sourceTree = SOURCE_ROOT; }; | |||
8EACAADD3A23DED3E252C92F /* juce_core */ /* juce_core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_core; path = ../../../../modules/juce_core; sourceTree = SOURCE_ROOT; }; | |||
92800676AF753D1A60108F11 /* BinaryData.h */ /* BinaryData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BinaryData.h; path = ../../JuceLibraryCode/BinaryData.h; sourceTree = SOURCE_ROOT; }; | |||
935CA85EF98714D3A17AE737 /* QuartzCore.framework */ /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; | |||
@@ -83,6 +82,7 @@ | |||
AFF729977947528F3E4AAA96 /* include_juce_cryptography.mm */ /* include_juce_cryptography.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_cryptography.mm; path = ../../JuceLibraryCode/include_juce_cryptography.mm; sourceTree = SOURCE_ROOT; }; | |||
B5433B00F012AD87AADBFCD6 /* juce_cryptography */ /* juce_cryptography */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_cryptography; path = ../../../../modules/juce_cryptography; sourceTree = SOURCE_ROOT; }; | |||
B76F10A7778664E164A01934 /* juce_audio_basics */ /* juce_audio_basics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_basics; path = ../../../../modules/juce_audio_basics; sourceTree = SOURCE_ROOT; }; | |||
B9B80E3572715F63FFC3678B /* ClientComponent.h */ /* ClientComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClientComponent.h; path = ../../Source/ClientComponent.h; sourceTree = SOURCE_ROOT; }; | |||
BA2E40409255F1B078406221 /* juce_data_structures */ /* juce_data_structures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_data_structures; path = ../../../../modules/juce_data_structures; sourceTree = SOURCE_ROOT; }; | |||
C6E2284D86D93F1D9D5C7666 /* include_juce_audio_formats.mm */ /* include_juce_audio_formats.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_formats.mm; path = ../../JuceLibraryCode/include_juce_audio_formats.mm; sourceTree = SOURCE_ROOT; }; | |||
C78806A6727F44EACFDED4A5 /* Cocoa.framework */ /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; | |||
@@ -138,7 +138,7 @@ | |||
4FF648D72D6F1A78956CDA1B, | |||
77C0AC21C1028911123844FC, | |||
9982F39121710EFFD5FEEAEF, | |||
8E2F72AFA0CDA64F0C07F105, | |||
B9B80E3572715F63FFC3678B, | |||
25DEDA8C9F94A6C8DFC8E53E, | |||
); | |||
name = Source; | |||
@@ -2174,7 +2174,7 @@ | |||
<ItemGroup> | |||
<ClInclude Include="..\..\Source\Demos.h"/> | |||
<ClInclude Include="..\..\Source\MasterComponent.h"/> | |||
<ClInclude Include="..\..\Source\SlaveComponent.h"/> | |||
<ClInclude Include="..\..\Source\ClientComponent.h"/> | |||
<ClInclude Include="..\..\Source\SharedCanvas.h"/> | |||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"/> | |||
<ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/> | |||
@@ -2667,7 +2667,7 @@ | |||
<ClInclude Include="..\..\Source\MasterComponent.h"> | |||
<Filter>NetworkGraphicsDemo\Source</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\..\Source\SlaveComponent.h"> | |||
<ClInclude Include="..\..\Source\ClientComponent.h"> | |||
<Filter>NetworkGraphicsDemo\Source</Filter> | |||
</ClInclude> | |||
<ClInclude Include="..\..\Source\SharedCanvas.h"> | |||
@@ -71,7 +71,6 @@ | |||
7BE6330821794919A88ED8ED /* include_juce_gui_extra.mm */ /* include_juce_gui_extra.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_extra.mm; path = ../../JuceLibraryCode/include_juce_gui_extra.mm; sourceTree = SOURCE_ROOT; }; | |||
84B287BB2AD252B7D69AC47E /* include_juce_gui_basics.mm */ /* include_juce_gui_basics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_basics.mm; path = ../../JuceLibraryCode/include_juce_gui_basics.mm; sourceTree = SOURCE_ROOT; }; | |||
89583CD42AD218E9753DF11C /* juce_audio_devices */ /* juce_audio_devices */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_devices; path = ../../../../modules/juce_audio_devices; sourceTree = SOURCE_ROOT; }; | |||
8E2F72AFA0CDA64F0C07F105 /* SlaveComponent.h */ /* SlaveComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SlaveComponent.h; path = ../../Source/SlaveComponent.h; sourceTree = SOURCE_ROOT; }; | |||
8EACAADD3A23DED3E252C92F /* juce_core */ /* juce_core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_core; path = ../../../../modules/juce_core; sourceTree = SOURCE_ROOT; }; | |||
9193D2A3C463BEAA07FD424D /* CoreText.framework */ /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; | |||
92800676AF753D1A60108F11 /* BinaryData.h */ /* BinaryData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BinaryData.h; path = ../../JuceLibraryCode/BinaryData.h; sourceTree = SOURCE_ROOT; }; | |||
@@ -88,6 +87,7 @@ | |||
AFF729977947528F3E4AAA96 /* include_juce_cryptography.mm */ /* include_juce_cryptography.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_cryptography.mm; path = ../../JuceLibraryCode/include_juce_cryptography.mm; sourceTree = SOURCE_ROOT; }; | |||
B5433B00F012AD87AADBFCD6 /* juce_cryptography */ /* juce_cryptography */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_cryptography; path = ../../../../modules/juce_cryptography; sourceTree = SOURCE_ROOT; }; | |||
B76F10A7778664E164A01934 /* juce_audio_basics */ /* juce_audio_basics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_basics; path = ../../../../modules/juce_audio_basics; sourceTree = SOURCE_ROOT; }; | |||
B9B80E3572715F63FFC3678B /* ClientComponent.h */ /* ClientComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClientComponent.h; path = ../../Source/ClientComponent.h; sourceTree = SOURCE_ROOT; }; | |||
BA2E40409255F1B078406221 /* juce_data_structures */ /* juce_data_structures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_data_structures; path = ../../../../modules/juce_data_structures; sourceTree = SOURCE_ROOT; }; | |||
C6E2284D86D93F1D9D5C7666 /* include_juce_audio_formats.mm */ /* include_juce_audio_formats.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_formats.mm; path = ../../JuceLibraryCode/include_juce_audio_formats.mm; sourceTree = SOURCE_ROOT; }; | |||
C821C5805007FFDC2636BBE6 /* OpenGLES.framework */ /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; | |||
@@ -147,7 +147,7 @@ | |||
4FF648D72D6F1A78956CDA1B, | |||
77C0AC21C1028911123844FC, | |||
9982F39121710EFFD5FEEAEF, | |||
8E2F72AFA0CDA64F0C07F105, | |||
B9B80E3572715F63FFC3678B, | |||
25DEDA8C9F94A6C8DFC8E53E, | |||
); | |||
name = Source; | |||
@@ -10,8 +10,8 @@ | |||
<FILE id="xdUc9q" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/> | |||
<FILE id="Vjuvqu" name="MasterComponent.h" compile="0" resource="0" | |||
file="Source/MasterComponent.h"/> | |||
<FILE id="KbZNxO" name="SlaveComponent.h" compile="0" resource="0" | |||
file="Source/SlaveComponent.h"/> | |||
<FILE id="KbZNxO" name="ClientComponent.h" compile="0" resource="0" | |||
file="Source/ClientComponent.h"/> | |||
<FILE id="F7A4kl" name="SharedCanvas.h" compile="0" resource="0" file="Source/SharedCanvas.h"/> | |||
</GROUP> | |||
<FILE id="Ww6bQw" name="juce_icon.png" compile="0" resource="1" file="Source/juce_icon.png"/> | |||
@@ -25,19 +25,19 @@ | |||
/** | |||
This component runs in a slave process, draws the part of the canvas that this | |||
This component runs in a client process, draws the part of the canvas that this | |||
particular client covers, and updates itself when messages arrive from the master | |||
containing new canvas states. | |||
*/ | |||
class SlaveCanvasComponent : public Component, | |||
private OSCSender, | |||
private OSCReceiver, | |||
private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>, | |||
private AsyncUpdater, | |||
private Timer | |||
class ClientCanvasComponent : public Component, | |||
private OSCSender, | |||
private OSCReceiver, | |||
private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>, | |||
private AsyncUpdater, | |||
private Timer | |||
{ | |||
public: | |||
SlaveCanvasComponent (PropertiesFile& p, int windowIndex) : properties (p) | |||
ClientCanvasComponent (PropertiesFile& p, int windowIndex) : properties (p) | |||
{ | |||
{ | |||
String uuidPropName ("UUID" + String (windowIndex)); | |||
@@ -65,7 +65,7 @@ public: | |||
startTimer (2000); | |||
} | |||
~SlaveCanvasComponent() override | |||
~ClientCanvasComponent() override | |||
{ | |||
OSCReceiver::removeListener (this); | |||
} | |||
@@ -220,5 +220,5 @@ private: | |||
CriticalSection canvasLock; | |||
BlockPacketiser packetiser; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlaveCanvasComponent) | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ClientCanvasComponent) | |||
}; |
@@ -41,7 +41,7 @@ namespace | |||
} | |||
#include "SharedCanvas.h" | |||
#include "SlaveComponent.h" | |||
#include "ClientComponent.h" | |||
#include "Demos.h" | |||
#include "MasterComponent.h" | |||
@@ -107,7 +107,7 @@ public: | |||
: DocumentWindow ("JUCE Networked Graphics Demo", Colours::black, DocumentWindow::allButtons) | |||
{ | |||
setUsingNativeTitleBar (true); | |||
setContentOwned (new SlaveCanvasComponent (props, windowIndex), true); | |||
setContentOwned (new ClientCanvasComponent (props, windowIndex), true); | |||
setBounds (500, 100, getWidth(), getHeight()); | |||
setResizable (true, false); | |||
setVisible (true); | |||
@@ -23,7 +23,7 @@ | |||
namespace juce | |||
{ | |||
enum { magicMastSlaveConnectionHeader = 0x712baf04 }; | |||
enum { magicCoordWorkerConnectionHeader = 0x712baf04 }; | |||
static const char* startMessage = "__ipc_st"; | |||
static const char* killMessage = "__ipc_k_"; | |||
@@ -82,11 +82,11 @@ private: | |||
}; | |||
//============================================================================== | |||
struct ChildProcessMaster::Connection : public InterprocessConnection, | |||
private ChildProcessPingThread | |||
struct ChildProcessCoordinator::Connection : public InterprocessConnection, | |||
private ChildProcessPingThread | |||
{ | |||
Connection (ChildProcessMaster& m, const String& pipeName, int timeout) | |||
: InterprocessConnection (false, magicMastSlaveConnectionHeader), | |||
Connection (ChildProcessCoordinator& m, const String& pipeName, int timeout) | |||
: InterprocessConnection (false, magicCoordWorkerConnectionHeader), | |||
ChildProcessPingThread (timeout), | |||
owner (m) | |||
{ | |||
@@ -103,7 +103,7 @@ private: | |||
void connectionMade() override {} | |||
void connectionLost() override { owner.handleConnectionLost(); } | |||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } | |||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToWorker (m); } | |||
void pingFailed() override { connectionLost(); } | |||
void messageReceived (const MemoryBlock& m) override | |||
@@ -111,25 +111,34 @@ private: | |||
pingReceived(); | |||
if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage)) | |||
owner.handleMessageFromSlave (m); | |||
owner.handleMessageFromWorker (m); | |||
} | |||
ChildProcessMaster& owner; | |||
ChildProcessCoordinator& owner; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) | |||
}; | |||
//============================================================================== | |||
ChildProcessMaster::ChildProcessMaster() {} | |||
ChildProcessCoordinator::ChildProcessCoordinator() = default; | |||
ChildProcessMaster::~ChildProcessMaster() | |||
ChildProcessCoordinator::~ChildProcessCoordinator() | |||
{ | |||
killSlaveProcess(); | |||
killWorkerProcess(); | |||
} | |||
void ChildProcessMaster::handleConnectionLost() {} | |||
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 ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) | |||
bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb) | |||
{ | |||
if (connection != nullptr) | |||
return connection->sendMessage (mb); | |||
@@ -138,10 +147,10 @@ bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) | |||
return false; | |||
} | |||
bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, | |||
int timeoutMs, int streamFlags) | |||
bool ChildProcessCoordinator::launchWorkerProcess (const File& executable, const String& commandLineUniqueID, | |||
int timeoutMs, int streamFlags) | |||
{ | |||
killSlaveProcess(); | |||
killWorkerProcess(); | |||
auto pipeName = "p" + String::toHexString (Random().nextInt64()); | |||
@@ -157,7 +166,7 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin | |||
if (connection->isConnected()) | |||
{ | |||
sendMessageToSlave ({ startMessage, specialMessageSize }); | |||
sendMessageToWorker ({ startMessage, specialMessageSize }); | |||
return true; | |||
} | |||
@@ -167,11 +176,11 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin | |||
return false; | |||
} | |||
void ChildProcessMaster::killSlaveProcess() | |||
void ChildProcessCoordinator::killWorkerProcess() | |||
{ | |||
if (connection != nullptr) | |||
{ | |||
sendMessageToSlave ({ killMessage, specialMessageSize }); | |||
sendMessageToWorker ({ killMessage, specialMessageSize }); | |||
connection->disconnect(); | |||
connection.reset(); | |||
} | |||
@@ -180,11 +189,11 @@ void ChildProcessMaster::killSlaveProcess() | |||
} | |||
//============================================================================== | |||
struct ChildProcessSlave::Connection : public InterprocessConnection, | |||
private ChildProcessPingThread | |||
struct ChildProcessWorker::Connection : public InterprocessConnection, | |||
private ChildProcessPingThread | |||
{ | |||
Connection (ChildProcessSlave& p, const String& pipeName, int timeout) | |||
: InterprocessConnection (false, magicMastSlaveConnectionHeader), | |||
Connection (ChildProcessWorker& p, const String& pipeName, int timeout) | |||
: InterprocessConnection (false, magicCoordWorkerConnectionHeader), | |||
ChildProcessPingThread (timeout), | |||
owner (p) | |||
{ | |||
@@ -198,12 +207,12 @@ struct ChildProcessSlave::Connection : public InterprocessConnection, | |||
} | |||
private: | |||
ChildProcessSlave& owner; | |||
ChildProcessWorker& owner; | |||
void connectionMade() override {} | |||
void connectionLost() override { owner.handleConnectionLost(); } | |||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } | |||
bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToCoordinator (m); } | |||
void pingFailed() override { connectionLost(); } | |||
void messageReceived (const MemoryBlock& m) override | |||
@@ -219,20 +228,29 @@ private: | |||
if (isMessageType (m, startMessage)) | |||
return owner.handleConnectionMade(); | |||
owner.handleMessageFromMaster (m); | |||
owner.handleMessageFromCoordinator (m); | |||
} | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) | |||
}; | |||
//============================================================================== | |||
ChildProcessSlave::ChildProcessSlave() {} | |||
ChildProcessSlave::~ChildProcessSlave() {} | |||
ChildProcessWorker::ChildProcessWorker() = default; | |||
ChildProcessWorker::~ChildProcessWorker() = default; | |||
void ChildProcessWorker::handleConnectionMade() {} | |||
void ChildProcessWorker::handleConnectionLost() {} | |||
void ChildProcessSlave::handleConnectionMade() {} | |||
void ChildProcessSlave::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 ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) | |||
bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb) | |||
{ | |||
if (connection != nullptr) | |||
return connection->sendMessage (mb); | |||
@@ -241,9 +259,9 @@ bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) | |||
return false; | |||
} | |||
bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, | |||
const String& commandLineUniqueID, | |||
int timeoutMs) | |||
bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine, | |||
const String& commandLineUniqueID, | |||
int timeoutMs) | |||
{ | |||
auto prefix = getCommandLinePrefix (commandLineUniqueID); | |||
@@ -25,47 +25,47 @@ namespace juce | |||
//============================================================================== | |||
/** | |||
Acts as the slave end of a master/slave pair of connected processes. | |||
Acts as the worker end of a coordinator/worker pair of connected processes. | |||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app | |||
The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app | |||
to spawn a child process, and to manage a 2-way messaging connection to control it. | |||
To use the system, you need to create subclasses of both ChildProcessSlave and | |||
ChildProcessMaster. To instantiate the ChildProcessSlave object, you must | |||
To use the system, you need to create subclasses of both ChildProcessWorker and | |||
ChildProcessCoordinator. To instantiate the ChildProcessWorker object, you must | |||
add some code to your main() or JUCEApplication::initialise() function that | |||
calls the initialiseFromCommandLine() method to check the app's command-line | |||
parameters to see whether it's being launched as a child process. If this returns | |||
true then the slave process can be allowed to run, and its handleMessageFromMaster() | |||
true then the worker process can be allowed to run, and its handleMessageFromCoordinator() | |||
method will be called whenever a message arrives. | |||
The juce demo app has a good example of this class in action. | |||
@see ChildProcessMaster, InterprocessConnection, ChildProcess | |||
@see ChildProcessCoordinator, InterprocessConnection, ChildProcess | |||
@tags{Events} | |||
*/ | |||
class JUCE_API ChildProcessSlave | |||
class JUCE_API ChildProcessWorker | |||
{ | |||
public: | |||
/** Creates a non-connected slave process. | |||
Use initialiseFromCommandLine to connect to a master process. | |||
/** Creates a non-connected worker process. | |||
Use initialiseFromCommandLine to connect to a coordinator process. | |||
*/ | |||
ChildProcessSlave(); | |||
ChildProcessWorker(); | |||
/** Destructor. */ | |||
virtual ~ChildProcessSlave(); | |||
virtual ~ChildProcessWorker(); | |||
/** This checks some command-line parameters to see whether they were generated by | |||
ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. | |||
ChildProcessCoordinator::launchWorkerProcess(), and if so, connects to that coordinator process. | |||
In an exe that can be used as a child process, you should add some code to your | |||
main() or JUCEApplication::initialise() that calls this method. | |||
The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) | |||
that matches the string passed to ChildProcessMaster::launchSlaveProcess(). | |||
that matches the string passed to ChildProcessCoordinator::launchWorkerProcess(). | |||
The timeoutMs parameter lets you specify how long the child process is allowed | |||
to run without receiving a ping from the master before the master is considered to | |||
to run without receiving a ping from the coordinator before the coordinator is considered to | |||
have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout | |||
makes it use a default value. | |||
@@ -76,78 +76,86 @@ public: | |||
int timeoutMs = 0); | |||
//============================================================================== | |||
/** This will be called to deliver messages from the master process. | |||
/** This will be called to deliver messages from the coordinator process. | |||
The call will probably be made on a background thread, so be careful with your | |||
thread-safety! You may want to respond by sending back a message with | |||
sendMessageToMaster() | |||
sendMessageToCoordinator() | |||
*/ | |||
virtual void handleMessageFromMaster (const MemoryBlock&) = 0; | |||
virtual void handleMessageFromCoordinator (const MemoryBlock& mb); | |||
/** This will be called when the master process finishes connecting to this slave. | |||
[[deprecated ("Replaced by handleMessageFromCoordinator.")]] | |||
virtual void handleMessageFromMaster (const MemoryBlock&) {} | |||
/** This will be called when the coordinator process finishes connecting to this worker. | |||
The call will probably be made on a background thread, so be careful with your thread-safety! | |||
*/ | |||
virtual void handleConnectionMade(); | |||
/** This will be called when the connection to the master process is lost. | |||
/** This will be called when the connection to the coordinator process is lost. | |||
The call may be made from any thread (including the message thread). | |||
Typically, if your process only exists to act as a slave, you should probably exit | |||
Typically, if your process only exists to act as a worker, you should probably exit | |||
when this happens. | |||
*/ | |||
virtual void handleConnectionLost(); | |||
/** Tries to send a message to the master process. | |||
/** Tries to send a message to the coordinator process. | |||
This returns true if the message was sent, but doesn't check that it actually gets | |||
delivered at the other end. If successful, the data will emerge in a call to your | |||
ChildProcessMaster::handleMessageFromSlave(). | |||
ChildProcessCoordinator::handleMessageFromWorker(). | |||
*/ | |||
bool sendMessageToMaster (const MemoryBlock&); | |||
bool sendMessageToCoordinator (const MemoryBlock&); | |||
[[deprecated ("Replaced by sendMessageToCoordinator.")]] | |||
bool sendMessageToMaster (const MemoryBlock& mb) { return sendMessageToCoordinator (mb); } | |||
private: | |||
struct Connection; | |||
std::unique_ptr<Connection> connection; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessWorker) | |||
}; | |||
using ChildProcessSlave [[deprecated ("Replaced by ChildProcessWorker.")]] = ChildProcessWorker; | |||
//============================================================================== | |||
/** | |||
Acts as the master in a master/slave pair of connected processes. | |||
Acts as the coordinator in a coordinator/worker pair of connected processes. | |||
The ChildProcessSlave and ChildProcessMaster classes make it easy for an app | |||
The ChildProcessWorker and ChildProcessCoordinator classes make it easy for an app | |||
to spawn a child process, and to manage a 2-way messaging connection to control it. | |||
To use the system, you need to create subclasses of both ChildProcessSlave and | |||
ChildProcessMaster. When you want your master process to launch the slave, you | |||
just call launchSlaveProcess(), and it'll attempt to launch the executable that | |||
To use the system, you need to create subclasses of both ChildProcessWorker and | |||
ChildProcessCoordinator. When you want your coordinator process to launch the worker, you | |||
just call launchWorkerProcess(), and it'll attempt to launch the executable that | |||
you specify (which may be the same exe), and assuming it has been set-up to | |||
correctly parse the command-line parameters (see ChildProcessSlave) then a | |||
correctly parse the command-line parameters (see ChildProcessWorker) then a | |||
two-way connection will be created. | |||
The juce demo app has a good example of this class in action. | |||
@see ChildProcessSlave, InterprocessConnection, ChildProcess | |||
@see ChildProcessWorker, InterprocessConnection, ChildProcess | |||
@tags{Events} | |||
*/ | |||
class JUCE_API ChildProcessMaster | |||
class JUCE_API ChildProcessCoordinator | |||
{ | |||
public: | |||
/** Creates an uninitialised master process object. | |||
Use launchSlaveProcess to launch and connect to a child process. | |||
/** Creates an uninitialised coordinator process object. | |||
Use launchWorkerProcess to launch and connect to a child process. | |||
*/ | |||
ChildProcessMaster(); | |||
ChildProcessCoordinator(); | |||
/** Destructor. | |||
Note that the destructor calls killSlaveProcess(), but doesn't wait for | |||
Note that the destructor calls killWorkerProcess(), but doesn't wait for | |||
the child process to finish terminating. | |||
*/ | |||
virtual ~ChildProcessMaster(); | |||
virtual ~ChildProcessCoordinator(); | |||
/** Attempts to launch and connect to a slave process. | |||
/** Attempts to launch and connect to a worker process. | |||
This will start the given executable, passing it a special command-line | |||
parameter based around the commandLineUniqueID string, which must be a | |||
short alphanumeric string (no spaces!) that identifies your app. The exe | |||
that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() | |||
that gets launched must respond by calling ChildProcessWorker::initialiseFromCommandLine() | |||
in its startup code, and must use a matching ID to commandLineUniqueID. | |||
The timeoutMs parameter lets you specify how long the child process is allowed | |||
@@ -156,37 +164,55 @@ public: | |||
it use a default value. | |||
If this all works, the method returns true, and you can begin sending and | |||
receiving messages with the slave process. | |||
receiving messages with the worker process. | |||
If a child process is already running, this will call killSlaveProcess() and | |||
If a child process is already running, this will call killWorkerProcess() and | |||
start a new one. | |||
*/ | |||
bool launchWorkerProcess (const File& executableToLaunch, | |||
const String& commandLineUniqueID, | |||
int timeoutMs = 0, | |||
int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); | |||
[[deprecated ("Replaced by launchWorkerProcess.")]] | |||
bool launchSlaveProcess (const File& executableToLaunch, | |||
const String& commandLineUniqueID, | |||
int timeoutMs = 0, | |||
int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr); | |||
int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr) | |||
{ | |||
return launchWorkerProcess (executableToLaunch, commandLineUniqueID, timeoutMs, streamFlags); | |||
} | |||
/** Sends a kill message to the slave, and disconnects from it. | |||
/** Sends a kill message to the worker, and disconnects from it. | |||
Note that this won't wait for it to terminate. | |||
*/ | |||
void killSlaveProcess(); | |||
void killWorkerProcess(); | |||
/** This will be called to deliver a message from the slave process. | |||
[[deprecated ("Replaced by killWorkerProcess.")]] | |||
void killSlaveProcess() { killWorkerProcess(); } | |||
/** This will be called to deliver a message from the worker process. | |||
The call will probably be made on a background thread, so be careful with your thread-safety! | |||
*/ | |||
virtual void handleMessageFromSlave (const MemoryBlock&) = 0; | |||
virtual void handleMessageFromWorker (const MemoryBlock&); | |||
[[deprecated ("Replaced by handleMessageFromWorker")]] | |||
virtual void handleMessageFromSlave (const MemoryBlock&) {} | |||
/** This will be called when the slave process dies or is somehow disconnected. | |||
/** This will be called when the worker process dies or is somehow disconnected. | |||
The call will probably be made on a background thread, so be careful with your thread-safety! | |||
*/ | |||
virtual void handleConnectionLost(); | |||
/** Attempts to send a message to the slave process. | |||
/** Attempts to send a message to the worker process. | |||
This returns true if the message was dispatched, but doesn't check that it actually | |||
gets delivered at the other end. If successful, the data will emerge in a call to | |||
your ChildProcessSlave::handleMessageFromMaster(). | |||
your ChildProcessWorker::handleMessageFromCoordinator(). | |||
*/ | |||
bool sendMessageToSlave (const MemoryBlock&); | |||
bool sendMessageToWorker (const MemoryBlock&); | |||
[[deprecated ("Replaced by sendMessageToWorker.")]] | |||
bool sendMessageToSlave (const MemoryBlock& mb) { return sendMessageToWorker (mb); } | |||
private: | |||
std::unique_ptr<ChildProcess> childProcess; | |||
@@ -194,7 +220,9 @@ private: | |||
struct Connection; | |||
std::unique_ptr<Connection> connection; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessCoordinator) | |||
}; | |||
using ChildProcessMaster [[deprecated ("Replaced by ChildProcessCoordinator.")]] = ChildProcessCoordinator; | |||
} // namespace juce |