The Go runtime has annoying behaviour around setting and clearing
O_NONBLOCK: exec.Cmd.Start() ends up calling os.File.Fd() for any
file in exec.Cmd.ExtraFiles. os.File.Fd() disables both the use
of the runtime poller for the file and clears O_NONBLOCK from
the underlying open file descriptor.
Disabling the runtime poller is bad because it makes operating on
those files less efficient. We can work around this by always
working on duplicate os.File. Since the library itself never
reads or writes from the files it doesn't matter whether the
runtime poller is enabled or not.
Clearing O_NONBLOCK is much more annoying, since it's shared by
all fds pointing to the same open file descriptor. This can lead
to hard to diagnose hangs in code invoking syscalls on the file
descriptors, like read or accept. Even more troubling, File.Close
doesn't interrupt these syscalls anymore. Depending on how the
application is structured this can lead to indefinite hangs.
Fix this by writing a simplified wrapper around syscall.StartProcess.
It turns out that we don't need most features of exec.Cmd anyways.
A full restart is needed to make files non-blocking again in
deployed processes.
The Go runtime has annoying behaviour around setting and clearing O_NONBLOCK: exec.Cmd.Start() ends up calling os.File.Fd() for any file in exec.Cmd.ExtraFiles. os.File.Fd() disables both the use of the runtime poller for the file and clears O_NONBLOCK from the underlying open file descriptor.
Disabling the runtime poller is bad because it makes operating on those files less efficient. We can work around this by always working on duplicate os.File. Since the library itself never reads or writes from the files it doesn't matter whether the runtime poller is enabled or not.
Clearing O_NONBLOCK is much more annoying, since it's shared by all fds pointing to the same open file descriptor. This can lead to hard to diagnose hangs in code invoking syscalls on the file descriptors, like read or accept. Even more troubling, File.Close doesn't interrupt these syscalls anymore. Depending on how the application is structured this can lead to indefinite hangs.
Fix this by writing a simplified wrapper around syscall.StartProcess. It turns out that we don't need most features of exec.Cmd anyways.
A full restart is needed to make files non-blocking again in deployed processes.