Deadlock happens when there are 2 or more resources, and in order to get the resources, we need to acquire lock first, then the order of acquiring lock is different. Eg: Process A wants to get value of variable 1 and then variable 2, meanwhile Process B wants to get value of variable 2, then variable 1. Before accessing the variable, each process will acquire the lock.
A picture is worth a thousand words, right? Here it is:
Here is the sample code to trigger deadlock in go. This example is inspired by Concurrency in Go book.
package main
import (
"log"
"sync"
"time"
)
type Something struct {
val int
lock sync.Mutex
}
func main() {
a := Something{
val: 12,
}
b := Something{
val: 13,
}
var wg sync.WaitGroup
sum := func(a, b *Something) {
defer wg.Done()
a.lock.Lock()
defer a.lock.Unlock()
time.Sleep(1 * time.Second)
b.lock.Lock()
defer b.lock.Unlock()
log.Printf("sum: %d\n", a.val+b.val)
}
wg.Add(2)
go sum(&a, &b)
go sum(&b, &a)
wg.Wait()
log.Printf("done")
}
Result:
dickynovanto@dickys-macbook % go run .
fatal error: all goroutines are asleep - deadlock!
Using time.Sleep
above makes the deadlock easily simulated. However, I can show you that the above scenario can happen as well without using time.Sleep
. We just need to run the procedures quite a lot of time and with “luck”, the deadlock will happen.
package main
import (
"log"
"sync"
)
type Something struct {
val int
lock sync.Mutex
}
func main() {
a := Something{
val: 12,
}
b := Something{
val: 13,
}
var wg sync.WaitGroup
sum := func(a, b *Something) {
defer wg.Done()
a.lock.Lock()
defer a.lock.Unlock()
// log.Printf("sleeping")
// time.Sleep(2 * time.Second)
// log.Printf("after sleeping")
b.lock.Lock()
defer b.lock.Unlock()
log.Printf("sum: %d\n", a.val+b.val)
}
for i := 0; i < 10000; i++ {
log.Printf("i: %v", i)
wg.Add(2)
go sum(&a, &b)
go sum(&b, &a)
wg.Wait()
}
log.Printf("done")
}
The triggering of deadlock is not deterministic in this case, sometimes, only after thousands of iteration, the deadlock can be triggered. One of the result:
2022/05/23 08:46:00 i: 0
...
2022/05/23 08:46:00 sum: 25
2022/05/23 08:46:00 i: 5461
fatal error: all goroutines are asleep - deadlock!