OpenShot Audio Library | OpenShotAudio: juce_ConnectedChildProcess.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_ConnectedChildProcess.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26enum { magicCoordWorkerConnectionHeader = 0x712baf04 };
27
28static const char* startMessage = "__ipc_st";
29static const char* killMessage = "__ipc_k_";
30static const char* pingMessage = "__ipc_p_";
31enum { specialMessageSize = 8, defaultTimeoutMs = 8000 };
32
33static bool isMessageType (const MemoryBlock& mb, const char* messageType) noexcept
34{
35 return mb.matches (messageType, (size_t) specialMessageSize);
36}
37
38static String getCommandLinePrefix (const String& commandLineUniqueID)
39{
40 return "--" + commandLineUniqueID + ":";
41}
42
43//==============================================================================
44// This thread sends and receives ping messages every second, so that it
45// can find out if the other process has stopped running.
46struct ChildProcessPingThread : public Thread,
47 private AsyncUpdater
48{
49 ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout)
50 {
51 pingReceived();
52 }
53
54 void startPinging() { startThread (Priority::low); }
55
56 void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; }
57 void triggerConnectionLostMessage() { triggerAsyncUpdate(); }
58
59 virtual bool sendPingMessage (const MemoryBlock&) = 0;
60 virtual void pingFailed() = 0;
61
62 int timeoutMs;
63
64 using AsyncUpdater::cancelPendingUpdate;
65
66private:
67 Atomic<int> countdown;
68
69 void handleAsyncUpdate() override { pingFailed(); }
70
71 void run() override
72 {
73 while (! threadShouldExit())
74 {
75 if (--countdown <= 0 || ! sendPingMessage ({ pingMessage, specialMessageSize }))
76 {
77 triggerConnectionLostMessage();
78 break;
79 }
80
81 wait (1000);
82 }
83 }
84
85 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread)
86};
87
88//==============================================================================
89struct ChildProcessCoordinator::Connection final : public InterprocessConnection,
90 private ChildProcessPingThread
91{
92 Connection (ChildProcessCoordinator& m, const String& pipeName, int timeout)
93 : InterprocessConnection (false, magicCoordWorkerConnectionHeader),
94 ChildProcessPingThread (timeout),
95 owner (m)
96 {
97 createPipe (pipeName, timeoutMs);
98 }
99
100 ~Connection() override
101 {
102 cancelPendingUpdate();
103 stopThread (10000);
104 }
105
106 using ChildProcessPingThread::startPinging;
107
108private:
109 void connectionMade() override {}
110 void connectionLost() override { owner.handleConnectionLost(); }
111
112 bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToWorker (m); }
113 void pingFailed() override { connectionLost(); }
114
115 void messageReceived (const MemoryBlock& m) override
116 {
117 pingReceived();
118
119 if (m.getSize() != specialMessageSize || ! isMessageType (m, pingMessage))
120 owner.handleMessageFromWorker (m);
121 }
122
123 ChildProcessCoordinator& owner;
124
125 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
126};
127
128//==============================================================================
129 ChildProcessCoordinator::ChildProcessCoordinator() = default;
130
131ChildProcessCoordinator::~ChildProcessCoordinator()
132{
133 killWorkerProcess();
134}
135
136 void ChildProcessCoordinator::handleConnectionLost() {}
137
138 void ChildProcessCoordinator::handleMessageFromWorker (const MemoryBlock& mb)
139{
140 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
141 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
142 handleMessageFromSlave (mb);
143 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
144 JUCE_END_IGNORE_WARNINGS_MSVC
145}
146
147 bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb)
148{
149 if (connection != nullptr)
150 return connection->sendMessage (mb);
151
152 jassertfalse; // this can only be used when the connection is active!
153 return false;
154}
155
156 bool ChildProcessCoordinator::launchWorkerProcess (const File& executable, const String& commandLineUniqueID,
157 int timeoutMs, int streamFlags)
158{
159 killWorkerProcess();
160
161 auto pipeName = "p" + String::toHexString (Random().nextInt64());
162
163 StringArray args;
164 args.add (executable.getFullPathName());
165 args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName);
166
167 childProcess = [&]() -> std::shared_ptr<ChildProcess>
168 {
169 if ((SystemStats::getOperatingSystemType() & SystemStats::Linux) != 0)
170 return ChildProcessManager::getInstance()->createAndStartManagedChildProcess (args, streamFlags);
171
172 auto p = std::make_shared<ChildProcess>();
173
174 if (p->start (args, streamFlags))
175 return p;
176
177 return nullptr;
178 }();
179
180 if (childProcess != nullptr)
181 {
182 connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
183
184 if (connection->isConnected())
185 {
186 connection->startPinging();
187 sendMessageToWorker ({ startMessage, specialMessageSize });
188 return true;
189 }
190
191 connection.reset();
192 }
193
194 return false;
195}
196
197 void ChildProcessCoordinator::killWorkerProcess()
198{
199 if (connection != nullptr)
200 {
201 sendMessageToWorker ({ killMessage, specialMessageSize });
202 connection->disconnect();
203 connection.reset();
204 }
205
206 childProcess.reset();
207}
208
209//==============================================================================
210struct ChildProcessWorker::Connection final : public InterprocessConnection,
211 private ChildProcessPingThread
212{
213 Connection (ChildProcessWorker& p, const String& pipeName, int timeout)
214 : InterprocessConnection (false, magicCoordWorkerConnectionHeader),
215 ChildProcessPingThread (timeout),
216 owner (p)
217 {
218 connectToPipe (pipeName, timeoutMs);
219 }
220
221 ~Connection() override
222 {
223 cancelPendingUpdate();
224 stopThread (10000);
225 disconnect();
226 }
227
228 using ChildProcessPingThread::startPinging;
229
230private:
231 ChildProcessWorker& owner;
232
233 void connectionMade() override {}
234 void connectionLost() override { owner.handleConnectionLost(); }
235
236 bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToCoordinator (m); }
237 void pingFailed() override { connectionLost(); }
238
239 void messageReceived (const MemoryBlock& m) override
240 {
241 pingReceived();
242
243 if (isMessageType (m, pingMessage))
244 return;
245
246 if (isMessageType (m, killMessage))
247 return triggerConnectionLostMessage();
248
249 if (isMessageType (m, startMessage))
250 return owner.handleConnectionMade();
251
252 owner.handleMessageFromCoordinator (m);
253 }
254
255 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection)
256};
257
258//==============================================================================
259 ChildProcessWorker::ChildProcessWorker() = default;
260 ChildProcessWorker::~ChildProcessWorker() = default;
261
262 void ChildProcessWorker::handleConnectionMade() {}
263 void ChildProcessWorker::handleConnectionLost() {}
264
265 void ChildProcessWorker::handleMessageFromCoordinator (const MemoryBlock& mb)
266{
267 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
268 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
269 handleMessageFromMaster (mb);
270 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
271 JUCE_END_IGNORE_WARNINGS_MSVC
272}
273
274 bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb)
275{
276 if (connection != nullptr)
277 return connection->sendMessage (mb);
278
279 jassertfalse; // this can only be used when the connection is active!
280 return false;
281}
282
283 bool ChildProcessWorker::initialiseFromCommandLine (const String& commandLine,
284 const String& commandLineUniqueID,
285 int timeoutMs)
286{
287 auto prefix = getCommandLinePrefix (commandLineUniqueID);
288
289 if (commandLine.trim().startsWith (prefix))
290 {
291 auto pipeName = commandLine.fromFirstOccurrenceOf (prefix, false, false)
292 .upToFirstOccurrenceOf (" ", false, false).trim();
293
294 if (pipeName.isNotEmpty())
295 {
296 connection.reset (new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs));
297
298 if (connection->isConnected())
299 connection->startPinging();
300 else
301 connection.reset();
302 }
303 }
304
305 return connection != nullptr;
306}
307
308} // namespace juce
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::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::startThread
bool startThread()
Definition juce_Thread.cpp:145
juce::Thread::threadShouldExit
bool threadShouldExit() const
Definition juce_Thread.cpp:202
juce::Thread::Priority::low
@ low