FWIW I also started writing a init-like server in Go. One thing I ran into was that Go's APIs sort of coerce you into having an extra thread per process. runsit also has this issue. See line 489 of runsit.go, pasted below.<p>You can do it non-portably in Go by using os.ForkExec and Wait4(-1). The portable exec package assumes you will call Wait(pid), and not Wait(-1), which basically implies using a thread per process. Go's runtime isn't magic -- if you call libc/syscall wait(), an entire thread will be blocked, and the runtime can't use it for anything else. In this case this is the lifetime of an entire process, which is forever for server processes.<p>I'm pretty sure nobody would use a real PID 1 that burned a thread per process (systemd, upstart, etc.). But yes, for most use cases, in the grand scheme of things, it's probably not a big deal. I suppose Linux has an O(1) scheduler, although I'm not quite sure how this affects scheduling (interested in any comments).<p>But this goes to show that portable APIs are awkward and obscure for low level code. Better to use raw Unix APIs for something like an init server. Python and Java have similar problems.<p>IMO all interesting code nowadays is POSIX-like, so we should drop the pretension of portability and simplify our lives. Unix works.<p><pre><code> // run in its own goroutine
func (in *TaskInstance) awaitDeath() {
in.waitErr = in.cmd.Wait() // ties up an OS thread for the lifetime of a process
...
}</code></pre>