Search code examples
gobroadcastgoroutine

Can't handle a go routine deadloock


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


Solution

  • 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.