Home > Software engineering >  Channel is not recieving data or giving dead block
Channel is not recieving data or giving dead block

Time:07-12

I've the below code:

//main.go
package main

import (
    "edriven/events"
    "fmt"
    "math"
    "time"
)

func main() {
    fmt.Println("Starting")
    events.Wg.Add(1)
    go events.User.Trigger("new", "Hasan")
    events.Wg.Add(1)
    go events.User.Trigger("name", []any{"Hasan", "Ali"})
    events.Wg.Add(1)
    go events.User.Trigger("new", "Ali")

    //for x := range <-events.Publish {
    //  fmt.Println(x)
    //}

    for {
        select {
        case x := <-events.Publish:
            fmt.Println(x)
        default:
            fmt.Println("waiting for data ...")
            time.Sleep((time.Duration(math.MaxInt64)))
        }
    }
}

And

//events/user.go
package events

import "fmt"

var User Events

func init() {
    User.register("new", func(payload ...any) {
        fmt.Println(payload[0])
        //message := make(map[string]string)
        //message["new"] = "done new"
        Publish <- "{'new':'done'}"
        Wg.Done()

    })

    User.register("name", func(payload ...any) {
        for index, person := range payload {
            fmt.Println(person, index)
        }
        //message := make(map[string]string)
        //message["name"] = "done name"
        Publish <- "{'name':'done'}" //message
        Wg.Done()
    })
}

And

//events/setup.go
package events

import "sync"

var Wg sync.WaitGroup
var Publish chan string

type Event struct {
    Name   string
    Action func(...any) // <-chan string // func(...any) ([]any, error)

}

type Events struct {
    handlers []Event
}

func (e *Events) register(name string, action func(...any)) {
    e.handlers = append(e.handlers, Event{
        Name:   name,
        Action: action,
    })
}

func (e *Events) Trigger(name string, payload ...any) {
    for _, event := range e.handlers {
        if event.Name == name {
            event.Action(payload)
        }
    }
}

The output I got is as below, that is nothing is exchanged through the channels

enter image description here

If I replaced the for { select {} } loop by the for x := range <-events.Publish { } loop then I get the below error:

PS D:\Deployment\event-driven> go run edriven
Starting
[Ali]
[Hasan]
[[Hasan Ali]] 0
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive (nil chan)]:
main.main()
        D:/Deployment/event-driven/main.go:17  0x1c5

goroutine 6 [chan send (nil chan)]:
edriven/events.init.0.func1({0xc000086010?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:12  0x6c
edriven/events.(*Events).Trigger(0x0?, {0xe45ca0, 0x3}, {0xc000086000, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34  0x129
created by main.main
        D:/Deployment/event-driven/main.go:11  0xb5

goroutine 7 [chan send (nil chan)]:
edriven/events.init.0.func2({0xc000180010?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:23  0x45
edriven/events.(*Events).Trigger(0x0?, {0xe45db9, 0x4}, {0xc000180000, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34  0x129
created by main.main
        D:/Deployment/event-driven/main.go:13  0x15d

goroutine 8 [chan send (nil chan)]:
edriven/events.init.0.func1({0xc000050260?, 0x1?, 0x1?})
        D:/Deployment/event-driven/events/user.go:12  0x6c
edriven/events.(*Events).Trigger(0x0?, {0xe45ca0, 0x3}, {0xc000050250, 0x1, 0x1})
        D:/Deployment/event-driven/events/setup.go:34  0x129
created by main.main
        D:/Deployment/event-driven/main.go:15  0x1aa
exit status 2
PS D:\Deployment\event-driven> 

CodePudding user response:

This block of code is problematic

for {
    select {
    case x := <- events.Publish:
        fmt.Println(x)
    default:
        fmt.Println("waiting for data ...")
        time.Sleep((time.Duration(math.MaxInt64)))
    }
}

When select is invoked and assuming the Publish channel is still empty, the default case will run and block the main loop forever with the time.Sleep statement. Hence, even if the Publish channel receives data from another go-routine, the main go-routine is still stuck on that Sleep statement.

Any time you want to combine a timed wait with a channel event, you can do this:

timerChannel := time.NewTimer(duration)

select {
case <-timerChannel.C:
    {
        // time out
    }
case x := <-events.Publish:
    {
        fmt.println(x)
    }
}

But since your intent appears to just block main from exiting, then it's even simpler:

for {
    x := <- events.Publish:  // blocks until Publish channel has data
    fmt.Println(x)
}

But as you called out, that leads to a deadlock because after your three go-routines after exited, there's nothing left to do.

Quick fix:

func main() {
    fmt.Println("Starting")
    events.Wg.Add(1)
    go events.User.Trigger("new", "Hasan")
    events.Wg.Add(1)
    go events.User.Trigger("name", []any{"Hasan", "Ali"})
    events.Wg.Add(1)
    go events.User.Trigger("new", "Ali")

    exitChannel := make(chan bool)
    go func() {
        events.Wg.Wait()
        close(exitChannel)
    }()

    canExit := false
    for !canExit {
        select {
        case x := <-events.Publish:
            {
                fmt.Println(x)
            }
        case <- exitChannel:
            {
                canExit = true
            }
        }
    }
}

As discussed in the comments, channel is required to be initialized, a make is missing, it has to be done as:

package events

import "sync"

var (
    Wg      sync.WaitGroup
    Publish chan string
)

func init() {
    Publish = make(chan string)
}
  •  Tags:  
  • go
  • Related