That part about the threading was a huge source of issues back in the early days. Goroutines could hop threads and it's the threads namespace/cgroup configuration that gets inherited when forking..
One of the gnarliest issues I recall was where network namespaces could occasionally not be cleaned up correctly. So "randomly" (but more likely under certain loads) the networking of a reused thread would get jacked and Docker could no longer pull images.
Plus Go's function for locking a goroutine to an OS thread was broken to the point where you could not safely rely on it for quite a while.
Never, ever, ever, write your own userland threading library.
(rule broken by almost every language in common use, unfortunately)
That’s an absurd statement. Optimizing every language around what a runc-like program would need is absurd.
Where does this advice come from? Most web servers would use a lot more resources if they didn't use userland threading, wouldn't they?
I think you're confusing userland threading (also known as green threads) with a regular thread pool implementation (that uses regular OS threads).
Because most web servers use OS threads. Nginx, Apache, litespeed, they all spin up `core count` threads but they can all work single threaded just fine as well.
No, I mean that Tokio, Go, etc., were built with general-purpose web servers in mind, and they decided that lightweight threads were necessary for that purpose because OS threads had too much overhead to be used per-request.
NGINX uses a state machine per task, which as far as I know is kind of what Rust's async functions end up desugaring to.