I have experienced different behaviour on Windows compared to macOS if an application creates a juce::InterProcessConnectionServer
and then creates a slave process using juce::ChildProcess
.
On windows,juce::ChildProcess
passes TRUE
as 5:th parameter when calling CreateProcess
so that any handle which is ineritable gets inherited.
When juce::InterProcessConnectionServer::stop
is called, then the thread is stuck in the call to accept
since the handle is not closed (it is open in the childprocess). On macOS a temporary socket is created and used to connect, so the call to accept
returns.
#if JUCE_WINDOWS
if (h != invalidSocket || connected)
closesocket (h);
// make sure any read process finishes before we delete the socket
CriticalSection::ScopedLockType lock (readLock);
connected = false;
#else
if (connected)
{
connected = false;
if (isListener)
{
// need to do this to interrupt the accept() function..
StreamingSocket temp;
temp.connect (IPAddress::local().toString(), portNumber, 1000);
}
}
I read that (Handle Inheritance - Win32 apps | Microsoft Learn) handles shouldn’t be inheritable by default but I can’t see that the handle is made so. Since the `temp.connect` solution is used on non Windows platforms, maybe it would be a good idea to add it also on Windows.
Here is an example that reproduce:
#include <thread>
#include "juce_core/juce_core.h"
#include "juce_events/juce_events.h"
class Connection : public juce::InterprocessConnection {
public:
Connection() {}
~Connection() {
disconnect();
}
void connectionMade() override {
puts("connection made");
}
void connectionLost() override {}
void messageReceived(const juce::MemoryBlock&) override {}
};
class ConnectionServer : public juce::InterprocessConnectionServer {
public:
juce::InterprocessConnection* createConnectionObject() override {
connections_.push_back(std::make_unique<Connection>());
return connections_.back().get();
}
private:
std::vector<std::unique_ptr<Connection>> connections_;
};
void doParentThings() {
ConnectionServer server;
const auto address = "127.0.0.1";
const int port = 46000;
server.beginWaitingForSocket(port, address);
juce::ChildProcess childprocess;
const auto exeFilePath =
juce::File::getSpecialLocation(juce::File::SpecialLocationType::currentExecutableFile)
.getFullPathName();
childprocess.start(juce::StringArray{exeFilePath, "make argument count 2"});
server.stop();
}
void doChildThings() {
// Sleep longer than the timeout when stopping the thread that calls accept in
// InterProcessConnectionServer.
std::this_thread::sleep_for(std::chrono::seconds(10));
}
int wmain(int argc, char**) {
argc != 2 ? doParentThings() : doChildThings();
return 0;
}