2022-11-04 22:11:33 +00:00
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/**
|
|
|
|
Acts as the worker end of a coordinator/worker pair of connected processes.
|
|
|
|
|
|
|
|
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 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 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 ChildProcessCoordinator, InterprocessConnection, ChildProcess
|
|
|
|
|
|
|
|
@tags{Events}
|
|
|
|
*/
|
|
|
|
class JUCE_API ChildProcessWorker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/** Creates a non-connected worker process.
|
|
|
|
Use initialiseFromCommandLine to connect to a coordinator process.
|
|
|
|
*/
|
|
|
|
ChildProcessWorker();
|
|
|
|
|
|
|
|
/** Destructor. */
|
|
|
|
virtual ~ChildProcessWorker();
|
|
|
|
|
|
|
|
/** This checks some command-line parameters to see whether they were generated by
|
|
|
|
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 ChildProcessCoordinator::launchWorkerProcess().
|
|
|
|
|
|
|
|
The timeoutMs parameter lets you specify how long the child process is allowed
|
|
|
|
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.
|
|
|
|
|
|
|
|
Returns true if the command-line matches and the connection is made successfully.
|
|
|
|
*/
|
|
|
|
bool initialiseFromCommandLine (const String& commandLine,
|
|
|
|
const String& commandLineUniqueID,
|
|
|
|
int timeoutMs = 0);
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/** 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
|
|
|
|
sendMessageToCoordinator()
|
|
|
|
*/
|
|
|
|
virtual void handleMessageFromCoordinator (const MemoryBlock& mb);
|
|
|
|
|
|
|
|
[[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 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 worker, you should probably exit
|
|
|
|
when this happens.
|
|
|
|
*/
|
|
|
|
virtual void handleConnectionLost();
|
|
|
|
|
|
|
|
/** 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
|
|
|
|
ChildProcessCoordinator::handleMessageFromWorker().
|
|
|
|
*/
|
|
|
|
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 (ChildProcessWorker)
|
|
|
|
};
|
|
|
|
|
|
|
|
using ChildProcessSlave [[deprecated ("Replaced by ChildProcessWorker.")]] = ChildProcessWorker;
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/**
|
|
|
|
Acts as the coordinator in a coordinator/worker pair of connected processes.
|
|
|
|
|
|
|
|
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 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 ChildProcessWorker) then a
|
|
|
|
two-way connection will be created.
|
|
|
|
|
|
|
|
The juce demo app has a good example of this class in action.
|
|
|
|
|
|
|
|
@see ChildProcessWorker, InterprocessConnection, ChildProcess
|
|
|
|
|
|
|
|
@tags{Events}
|
|
|
|
*/
|
|
|
|
class JUCE_API ChildProcessCoordinator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/** Creates an uninitialised coordinator process object.
|
|
|
|
Use launchWorkerProcess to launch and connect to a child process.
|
|
|
|
*/
|
|
|
|
ChildProcessCoordinator();
|
|
|
|
|
|
|
|
/** Destructor.
|
|
|
|
Note that the destructor calls killWorkerProcess(), but doesn't wait for
|
|
|
|
the child process to finish terminating.
|
|
|
|
*/
|
|
|
|
virtual ~ChildProcessCoordinator();
|
|
|
|
|
|
|
|
/** 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 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
|
|
|
|
to go without sending a ping before it is considered to have died and
|
|
|
|
handleConnectionLost() will be called. Passing <= 0 for this timeout makes
|
|
|
|
it use a default value.
|
|
|
|
|
|
|
|
If this all works, the method returns true, and you can begin sending and
|
|
|
|
receiving messages with the worker process.
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
return launchWorkerProcess (executableToLaunch, commandLineUniqueID, timeoutMs, streamFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sends a kill message to the worker, and disconnects from it.
|
|
|
|
Note that this won't wait for it to terminate.
|
|
|
|
*/
|
|
|
|
void killWorkerProcess();
|
|
|
|
|
|
|
|
[[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 handleMessageFromWorker (const MemoryBlock&);
|
|
|
|
|
|
|
|
[[deprecated ("Replaced by handleMessageFromWorker")]]
|
|
|
|
virtual void handleMessageFromSlave (const MemoryBlock&) {}
|
|
|
|
|
|
|
|
/** 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 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 ChildProcessWorker::handleMessageFromCoordinator().
|
|
|
|
*/
|
|
|
|
bool sendMessageToWorker (const MemoryBlock&);
|
|
|
|
|
|
|
|
[[deprecated ("Replaced by sendMessageToWorker.")]]
|
|
|
|
bool sendMessageToSlave (const MemoryBlock& mb) { return sendMessageToWorker (mb); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::unique_ptr<ChildProcess> childProcess;
|
|
|
|
|
|
|
|
struct Connection;
|
|
|
|
std::unique_ptr<Connection> connection;
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessCoordinator)
|
|
|
|
};
|
|
|
|
|
|
|
|
using ChildProcessMaster [[deprecated ("Replaced by ChildProcessCoordinator.")]] = ChildProcessCoordinator;
|
|
|
|
|
|
|
|
} // namespace juce
|