From e5255eb76cc4b01bcf8d8f6408db12a4a038a12a Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 7 Feb 2022 15:43:41 +0000 Subject: [PATCH] ChildProcessDemo: Fix crashes when stopping child process Previously, the child process could be deleted from its own handleConnectionLost() callback, which would attempt to stop the child process's thread from that same thread. Now, the demo will attempt to stop the coordinator thread from the main thread instead. --- examples/Utilities/ChildProcessDemo.h | 52 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/examples/Utilities/ChildProcessDemo.h b/examples/Utilities/ChildProcessDemo.h index e6eb544611..2b3c4a580a 100644 --- a/examples/Utilities/ChildProcessDemo.h +++ b/examples/Utilities/ChildProcessDemo.h @@ -98,7 +98,12 @@ public: testResultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain }); 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); + "with a child process, sending messages in the form of serialised ValueTree objects.") + newLine + + String ("In this demo, the child process will automatically quit if it fails to receive a ping message at least every ") + + String (timeoutSeconds) + + String (" seconds. To keep the process alive, press the \"") + + pingButton.getButtonText() + + String ("\" button periodically.") + newLine); setSize (500, 500); } @@ -136,10 +141,14 @@ public: { if (coordinatorProcess.get() == nullptr) { - coordinatorProcess.reset (new DemoCoordinatorProcess (*this)); + coordinatorProcess = std::make_unique (*this); - if (coordinatorProcess->launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile), demoCommandLineUID)) + if (coordinatorProcess->launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile), + demoCommandLineUID, + timeoutMillis)) + { logMessage ("Child process started"); + } } } @@ -166,11 +175,14 @@ public: // 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 + private DeletedAtShutdown, + private AsyncUpdater { public: DemoCoordinatorProcess (ChildProcessDemo& d) : demo (d) {} + ~DemoCoordinatorProcess() override { cancelPendingUpdate(); } + // This gets called when a message arrives from the worker process.. void handleMessageFromWorker (const MemoryBlock& mb) override { @@ -183,6 +195,11 @@ public: void handleConnectionLost() override { demo.logMessage ("Connection lost to child process!"); + triggerAsyncUpdate(); + } + + void handleAsyncUpdate() override + { demo.killChildProcess(); } @@ -203,7 +220,11 @@ public: //============================================================================== std::unique_ptr coordinatorProcess; + static constexpr auto timeoutSeconds = 10; + static constexpr auto timeoutMillis = timeoutSeconds * 1000; + private: + TextButton launchButton { "Launch Child Process" }; TextButton pingButton { "Send Ping" }; TextButton killButton { "Kill Child Process" }; @@ -268,8 +289,8 @@ public: } /* 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.. + 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. */ void handleConnectionLost() override { @@ -280,13 +301,13 @@ public: //============================================================================== /* The JUCEApplication::initialise method calls this function to allow the child process to launch when the command line parameters indicate that we're - being asked to run as a child process.. + being asked to run as a child process. */ bool invokeChildProcessDemo (const String& commandLine) { - std::unique_ptr worker (new DemoWorkerProcess()); + auto worker = std::make_unique(); - if (worker->initialiseFromCommandLine (commandLine, demoCommandLineUID)) + if (worker->initialiseFromCommandLine (commandLine, demoCommandLineUID, ChildProcessDemo::timeoutMillis)) { worker.release(); // allow the worker object to stay alive - it'll handle its own deletion. return true; @@ -316,7 +337,7 @@ bool invokeChildProcessDemo (const String& commandLine) if (invokeChildProcessDemo (commandLine)) return; - mainWindow.reset (new MainWindow ("ChildProcessDemo", new ChildProcessDemo())); + mainWindow = std::make_unique ("ChildProcessDemo", std::make_unique()); } void shutdown() override { mainWindow = nullptr; } @@ -325,13 +346,14 @@ bool invokeChildProcessDemo (const String& commandLine) class MainWindow : public DocumentWindow { public: - MainWindow (const String& name, Component* c) : DocumentWindow (name, - Desktop::getInstance().getDefaultLookAndFeel() - .findColour (ResizableWindow::backgroundColourId), - DocumentWindow::allButtons) + MainWindow (const String& name, std::unique_ptr c) + : DocumentWindow (name, + Desktop::getInstance().getDefaultLookAndFeel() + .findColour (ResizableWindow::backgroundColourId), + DocumentWindow::allButtons) { setUsingNativeTitleBar (true); - setContentOwned (c, true); + setContentOwned (c.release(), true); centreWithSize (getWidth(), getHeight());