juce::InterprocessConnection behaves differently on Windows and macOS

I have experienced different behaviour on Windows compared to macOS if an application creates a juce::InterProcessConnectionServerand then creates a slave process using juce::ChildProcess.

On windows,juce::ChildProcesspasses TRUEas 5:th parameter when calling CreateProcess so that any handle which is ineritable gets inherited.

When juce::InterProcessConnectionServer::stopis called, then the thread is stuck in the call to acceptsince 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;
}

Friendly bump :slight_smile: