> So the Go runtime doesn't automagically transform blocking system calls into non-blocking calls, but if one goroutine is waiting for I/O, other goroutines can run in the meantime.
But when using Go's standard file.read or socket.write, what you get is I/O multiplexing of goroutines (with epoll or kqueue) in the runtime.
Naturally, if you call a C library from Go that uses unix read or write and executes a blocking system call, that cannot be magically intercepted by the Go runtime.
Half-true. os.File is not multiplexed, so if there are multiple concurrent blocking reads, each of them will consume an OS thread until it completes. net.Conn, on the other hand, uses polling so only one OS thread is necessary. Technically speaking, this is a feature of the standard library and not the runtime system.
It seems like goroutines in Go don't do as much asynchronous stuff as Haskell's I/O manager and green threads do. Looking at Go sources, there is some async stuff w/epoll in the net library with sockets, but file.Read is indeed just a plain syscall.
It makes me wonder, what happens to a goroutine when a system call blocks. Go is supposed to mux many goroutines to a smaller number of OS threads, but what happens when one of those threads has been blocked in a system call?
There are plenty of vague descriptions on how goroutines work but none of the ones I found quickly explained what happens on a blocking system call that allows another goroutine to run, apart from hand-waving about "another goroutine running".
The Go runtime keeps track of the number of goroutines that are currently executing and ensures that this doesn't go above GOMAXPROCS. As soon as a goroutine enters a system call it is excluded from this count. If there aren't enough threads to run GOMAXPROCS goroutines the runtime may launch a new one.
See pkg/runtime/proc.c (entersyscall, ready, and matchmg).
But when using Go's standard file.read or socket.write, what you get is I/O multiplexing of goroutines (with epoll or kqueue) in the runtime.
Naturally, if you call a C library from Go that uses unix read or write and executes a blocking system call, that cannot be magically intercepted by the Go runtime.