Go

When does the Go runtime report "all goroutines are asleep - deadlock!" and when doesn't it?

Question 169MediumGo 1.22 to 1.25

The scheduler's checkdead function detects a deadlock only when no goroutine can make progress. That means there are no running Ms, and every remaining goroutine is parked with no timers pending and no network waiters that could wake one.

func main() {
	ch := make(chan int)
	ch <- 1 // fatal error: all goroutines are asleep - deadlock!
}

It does not fire in these cases:

  • Some other goroutine is still running or sleeping. A partial deadlock in two workers while an HTTP server runs is silent, and that is a leak.
  • A time.Ticker or pending timer exists, or goroutines are waiting on the netpoller. http.ListenAndServe alone keeps the program "alive".
  • Goroutines are blocked in cgo or syscalls.
  • select {} in main while a background goroutine is alive.

Also, locking a sync.Mutex you already hold is not re-entrant. In a busy program it just hangs.

What the interviewer wants to hear: do not count on the runtime to find deadlocks. Use lock ordering, timeouts and context, goroutine profiles, and testing/synctest, which reports goroutines blocked inside its bubble.

More on Goroutines & the Scheduler

All 35 Goroutines & the Scheduler questions