Go语言‘all goroutines are asleep - deadlock!’错误算法及等待图源码问询
Hey there! Awesome that you're digging into Go's internals even without professional experience—its runtime is definitely full of clever design choices. Let's tackle your questions one by one:
Go's runtime doesn't rely on a complex cycle-detection algorithm for this specific deadlock scenario. Instead, it uses a straightforward check of goroutine states:
- The Go scheduler tracks all goroutines (G) and their states: runnable (ready to execute), running (active on a thread), waiting (blocked on a channel, mutex, sleep, etc.), or dead.
- When the scheduler can't find any runnable or running goroutines to schedule, it triggers a deadlock check via the
checkdeadfunction. - This function iterates through all existing goroutines. If every single goroutine is in a waiting (or terminating/dead) state, and there's no way any of them can be woken up (since there are no active goroutines to perform wake-up actions—like sending to a channel another goroutine is waiting on, or unlocking a mutex), the runtime panics with the "all goroutines are asleep - deadlock!" message.
This is a "global deadlock" check, not a deep dive into circular waits between specific goroutines. It's optimized for Go's lightweight goroutine model, targeting the common scenario where all concurrent work has completely stalled.
Short answer: No, the Go runtime does not maintain an explicit, global directed graph tracking wait relationships between goroutines.
Unlike some other environments (like the JVM, which tracks lock ownership and wait chains for deadlock detection), Go's runtime doesn't build or maintain this kind of aggregated graph. The deadlock check we talked about earlier doesn't require it—it only needs to confirm if there are any active goroutines left that can unblock others.
That said, individual synchronization primitives do track their own waiting goroutines:
- A channel maintains queues of senders and receivers waiting on it.
- A
sync.Mutextracks the holding goroutine and a list of waiting goroutines.
But these are isolated per primitive, not compiled into a global wait graph.
Source Code Reference
If you want to explore the deadlock check logic, look into the runtime/proc.go file in the Go source code. The checkdead function is where this check happens—it loops through all goroutines using allgs (the global list of goroutines) and checks their status field. If all are in non-runnable states with no work left to execute, it calls throw("all goroutines are asleep - deadlock!").
内容的提问来源于stack exchange,提问作者nodakai

