Developer workspace

Three deadlock patterns we see in Go worker pools

January 2026 · Priya Naidoo

Go makes concurrency pleasant until shutdown. These patterns appeared in three separate audits last year; they share a theme—holding locks across channel operations.

1. Mutex while sending on a full channel

A worker locks a shared map, then pushes to a bounded channel without selecting on ctx.Done(). Producers block on send while consumers wait for the lock.

2. WaitGroup inside a lock

Coordinator goroutines call wg.Wait() while holding mu that workers need to decrement the group. Deployments hang until Kubernetes kills the pod.

3. Closing work channel before workers drain

Main closes the jobs channel immediately after cancel, but workers still log errors under lock. Fix: signal shutdown, wait for in-flight with a timeout, then close.

Review before merge

A concurrency design review catches these in diffs smaller than fifty lines—cheaper than a weekend outage.