As I was learning Go I initially wrote my short programs using an empty for loop to block the program from exiting as I ran my test functions with the go keyword. As my test/learning programs grew in size however, the entire program would sometimes freeze at random places and the debugger would disconnect, making debugging really hard.
I eventually learned that the cause was the empty for loop from a bit of discussion on the IRC, and replaced it with a blocking channel, but I never learned WHY other than it was something to do with how Go handles scheduling.
What mechanism in the background causes independent go-routines to lock entire programs, even when there are plenty of cores allocated to the program, if there is an empty infinite for loop?
For example, Issue #10958: runtime: tight loops should be preemptible #10958
Currently goroutines are only preemptible at function call points. Hence, it's possible to write a tight loop (e.g., a numerical kernel or a spin on an atomic) with no calls or allocation that arbitrarily delays preemption. This can result in arbitrarily long pause times as the GC waits for all goroutines to stop.
In unusual situations, this can even lead to deadlock when trying to stop the world. For example, the runtime's TestGoroutineParallelism tries to prevent GC during the test to avoid deadlock. It runs several goroutines in tight loops that communicate through a shared atomic variable. If the coordinator that starts these is paused part way through, it will deadlock.