I am trying to print in multiple go routines only when sync.Cond
does Broadcasts but the go routines are deadlocking. Is there any way the below code can be improved and are there any ways to identify why this code is deadlocking?
package main
import (
"fmt"
"sync"
)
type Button struct {
Clicked *sync.Cond
}
func subscribe(cond *sync.Cond, btnMessage string, wg *sync.WaitGroup) {
defer wg.Done()
cond.L.Lock()
defer cond.L.Unlock()
cond.Wait()
fmt.Println(btnMessage)
}
func test6() {
var wg sync.WaitGroup
button := Button{
Clicked: sync.NewCond(&sync.Mutex{}),
}
wg.Add(3)
go subscribe(button.Clicked, "Button 1", &wg)
go subscribe(button.Clicked, "Button 2", &wg)
go subscribe(button.Clicked, "Button 3", &wg)
// Start the goroutines before broadcasting
button.Clicked.Broadcast()
wg.Wait()
}
func main() {
test6()
}
I got this below deadlock error
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x466df7?)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:62 +0x25
sync.(*WaitGroup).Wait(0x405399?)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/waitgroup.go:139 +0x52
main.test6()
/home/runner/concurrency-in-go/main.go:36 +0x199
main.main()
/home/runner/concurrency-in-go/main.go:40 +0x17
goroutine 6 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x1)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x49962f, 0x8}, 0x0?)
/home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
/home/runner/concurrency-in-go/main.go:29 +0xdb
goroutine 7 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x2)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x499637, 0x8}, 0x0?)
/home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
/home/runner/concurrency-in-go/main.go:30 +0x12f
goroutine 8 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x0)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
/nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x49963f, 0x8}, 0x0?)
/home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
/home/runner/concurrency-in-go/main.go:31 +0x185
exit status 2
You're broadcasting before your goroutines even initialize. So you end up Broadcasting and then waiting again. If you insert a sleep in your main thread you can see it starts to work.
You should only wait in your goroutine when certain criteria are met. Depending on your actual use case, you might consider a different synchronization primitive.