Description
[Original title: Is process creation actually non-blocking?]
In #1104 I wrote:
the actual process startup is synchronous, so you could just as well have a synchronous version
But uh... it just occurred to me that I'm actually not sure if this is true! I mean, right now we just use subprocess.Popen
, which is indeed a synchronous interface. And on Unix, spawning a new process and getting a handle on it is generally super cheap – it's just fork
. The exec
is expensive, but that happens after the child has split off – the parent doesn't wait for it.
But on Windows, you call CreateProcess
, which I think might block the caller while doing all the disk access to set up the new process? Process creation on Windows are notoriously slow, and I don't know how much of that the parent process has to sit and wait for before CreateProcess
can return.
And even on Unix, you use vfork
, in which case the parent process is blocked until the exec
. And on recent Pythons, subprocess
uses posix_spawn
. On Linux this might use vfork
(I'm not actually sure?). And on macOS it uses a native posix_spawn
syscall, so who knows what that does. Again, this might not be a big deal... maybe the parent gets to go again the instant the child calls exec
, or sooner, without having to wait for any disk access or anything. But I'm not sure!
So... we should figure this out. Because if process creation is slow enough that we need to treat it as a blocking operation, we might need to change the process API to give it an async constructor. (Presumably by making Process.__init__
private, and adding await trio.open_process(...)
– similar to how we handle files.)