I am trying to broadcast a message to a channel, I just want it to send 5 messages. but I always get this error : fatal error: all goroutines are asleep - deadlock!
my code:
package main
import (
"log"
"sync"
broadcast "github.com/dustin/go-broadcast"
"github.com/pwaller/barrier"
)
//Message boradcasted
type Message struct {
y string
x int
}
var w sync.WaitGroup
var bar barrier.Barrier
func main() {
b := broadcast.NewBroadcaster(100)
w.Add(1)
go workerOne(b)
d := Message{"message :", 0}
go func() {
for i := 0; i < 5; i++ {
d.x = i
log.Printf("Sending %v", d)
b.Submit(d)
}
<-bar.Barrier()
b.Close()
}()
w.Wait()
}
func workerOne(b broadcast.Broadcaster) {
ch := make(chan interface{})
b.Register(ch)
for {
v, ok := <-ch
if ok {
log.Printf("workerOne() reading : %v", v)
} else {
log.Printf("i am here")
close(ch)
b.Unregister(ch)
bar.Fall()
w.Done()
return
}
}
}
the output :
2019/12/26 20:34:11 Sending {message : 0}
2019/12/26 20:34:11 Sending {message : 1}
2019/12/26 20:34:11 Sending {message : 2}
2019/12/26 20:34:11 Sending {message : 3}
2019/12/26 20:34:11 Sending {message : 4}
2019/12/26 20:34:11 workerOne() reading : {message : 0}
2019/12/26 20:34:11 workerOne() reading : {message : 1}
2019/12/26 20:34:11 workerOne() reading : {message : 2}
2019/12/26 20:34:11 workerOne() reading : {message : 3}
2019/12/26 20:34:11 workerOne() reading : {message : 4}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x59f530)
C:/Go/src/runtime/sema.go:56 +0x40
sync.(*WaitGroup).Wait(0x59f528)
C:/Go/src/sync/waitgroup.go:130 +0x6c
main.main()
C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:38 +0x107
goroutine 19 [select]:
github.com/dustin/go-broadcast.(*broadcaster).run(0xc000060420)
D:/gocode/src/github.com/dustin/go-broadcast/broadcaster.go:39 +0x10f
created by github.com/dustin/go-broadcast.NewBroadcaster
D:/gocode/src/github.com/dustin/go-broadcast/broadcaster.go:64 +0x103
goroutine 20 [chan receive]:
main.workerOne(0x4f1de0, 0xc000060420)
C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:46 +0xcd
created by main.main
C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:25 +0x8e
goroutine 21 [chan receive]:
main.main.func1(0xc000060440, 0x4f1de0, 0xc000060420)
C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:35 +0x168
created by main.main
C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:29 +0xf7
exit status 2
I tried everything but it doesn't go throw condition !ok in workerOne() function to close the channel and end waiting, but still have the same error
As Adrian noted in a comment, the barrier
package you are using is deprecated in favor of Go's included context
package. You should use context
instead. ( The broadcast
package you are using is not really doing you any good either, at the moment.)
The immediate problem seems pretty obvious, though: the (single) goroutine running workerOne
reads from a channel, and only if the channel is closed (so that ok
becomes false
) does it call bar.Fall()
to drop the barrier. Meanwhile, the (single) goroutine running the anonymous sender function:
go func() {
for i := 0; i < 5; i++ {
d.x = i
log.Printf("Sending %v", d)
b.Submit(d)
}
<-bar.Barrier()
b.Close()
}()
submits five items through the broadcast
package's (very) simple message pub/sub interface, then waits for the barrier to fall. The only goroutine that will drop the barrier—think of it as the only other gopher that can rescue you here—is the one running workerOne
. He is, right now, waiting in channel-receive:
v, ok := <-ch
You, in your main goroutine, are waiting via your sync.WaitGroup
variable:
w.Wait()
The gopher running the anonymous sender function is waiting:
<-bar.Barrier()
Who—which of these three gophers running or waiting in various Go routines—is going to signal, to the gopher in the anonymous sender, the v, ok := <-ch
should get a !ok
result? The only one who could is the gopher running workerOne
, but he's stuck.
The broadcast
package won't ever close your channel for you. Unregistering a channel just removes it from the broadcaster's output channels, which leaves the unregistered channel open; and closing the broadcaster entirely effectively just unregisters all channels, again leaving them open. So if you want your channel closed, you would have to do that yourself, somewhere else.
Alternatively, you could modify your workerOne
so that it doesn't depend on the channel being closed. For instance, your variable d
has an x int
and a y string
; you could pick one or both of these and decide that a string value reading "drop the barrier"—perhaps with some particular int
value as well—means "call bar.Fall()
now". If you do this, you're getting even less out of these two nonstandard packages, though.