How to pass a channel to a function as a parameter? - go

I made two attempts to pass a channel to a function as a parameter, but they both fail (deadlock):
Attempt 1:
func done(signal *chan bool) {
*signal <- true
}
func main() {
signal := make(chan bool)
done(&signal)
<-signal
fmt.Println("completed")
}
Attempt 2:
func done(signal chan bool) {
signal <- true
}
func main() {
signal := make(chan bool)
done(signal)
<-signal
fmt.Println("completed")
}
Well I am out of ideas. What should be the proper way to pass the channel to the function?

A channel is unbuffered by default, so when you send on a channel, that send will block
until someone receives the value. This is clearly the case in your code here, at the point where you you're doing signal <- true , there's noone that can or ever will receive on the same channel.
You can create a goroutine that does the sending, that way execution in main() continues and there will be someone that's receiving the value:
go done(signal)
Or you can create a buffered channel, so sending on a channel doesn't block if there's room for the value in the channel:
signal := make(chan bool, 1)

done(&signal) is called synchronously. Maybe what you wanted to do is to call it asynchronously?
to do so, put the keyword go in front of the function call
go done(&signal)
The main thread will block until the done function writes to the channel. And the done method will block on writing to the channel until the main thread reads the channel.

Related

Why does sending to channel is blocked when a panic occurs?

I am going to show a simple compilable code snipped where I get weird behaviour: after I intentionally cause a panic in processData (because I pass nil-pointer) the sending to channel l.done is blocked!
package main
import (
"context"
"fmt"
"time"
)
type Loop struct {
done chan struct{}
}
type Result struct {
ID string
}
func NewLoop() *Loop {
return &Loop{
done: make(chan struct{}),
}
}
func (l *Loop) StartAsync(ctx context.Context) {
go func() {
defer func() {
l.done <- struct{}{} // BLOCKED! But I allocated it in NewLoop ctor
fmt.Sprintf("done")
}()
for {
/**/
var data *Result
l.processData(ctx, data) // passed nil
}
}()
}
func (l *Loop) processData(ctx context.Context, data *Result) {
_ = fmt.Sprintf("%s", data.ID) // intentional panic - OK
}
func main() {
l := NewLoop()
l.StartAsync(context.Background())
time.Sleep(10 * time.Second)
}
I can recover() a panic before sending to channel and I get correct error message.
What does happen with channel? It it was closed, I would get panic on sending to closed channel
It's blocking because there isn't anything receiving from the channel. Both the receive & the send operations on an initialized and unbuffered channel will block indefinitely if the opposite operation is missing. I.e. a send to a channel will block until another goroutine receives from that channel, and, likewise, a receive from a channel will block until another goroutine sends to that channel.
So basically
done := new(chan struct{})
done<-struct{}{}
will block forever unless there is another goroutine that receives from the channel, i.e. <-done. That's how channels are supposed to behave. That's how the languages specification defines their behaviour.
about the possible fixes :
given the name of your channel, you may want to run close(l.done) rather than l.done <- struct{}{}.
using a buffered channel and l.done <- struct{}{} on completion : only one call to <-l.done will be unblocked.
Suppose you have some code looking like :
l := NewLoop()
go func(){
<-l.done
closeLoggers()
}()
go func(){
<-l.done
closeDatabase()
}()
sending one item on the done channel will make that only one consumer will receive it, and in the above example only one of the two actions will be triggered when the loop completes.
using close(l.done) : once a channel is closed all calls to receive from it will proceed.
In the above example, all actions will proceed.
As a side note: there is no need for a buffer if you use a channel only for its "open/closed" state.

golang channels deadlock before receiving values

I'm not understanding why this doesn't work https://play.golang.org/p/_ALPii0pXV6 but this https://play.golang.org/p/vCOjAr-o54e works.
As I understand the goroutine asynchronously sends to value true to a and 12 to b. While in the main function, a is blocked, until it receives a value. Why is it that when I rearrange it to have b is blocked before a, it results in a deadlock?
Go channels are unbuffered by default. That means that it cannot send on a channel until the receiver is reading the channel. This is actually the Go preferred mode. It's more efficient than buffered channels in most cases.
What that means for your first code is that the goroutine cannot proceed to write to channel b until it completes the write to channel a. It cannot do that until the main goroutine reads a.
Go by Example explains that, by default, channel sending and receiving waits until both the sending routine and the receiving routine are ready. This blocking is made obvious by the following example:
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
This code results in a deadlock because the only goroutine (the main one) is stuck at ch <- 1, waiting for another goroutine to receive. Little does it know that we are expecting it to be the receiver at the next line.
This explains why your first example does not work, because the other goroutine doesn't send on b until its send operation on a has completed. But the main routine won't receive on a until it's received on b! So both are stuck waiting forever.
To read more about this kind of operation (called a synchronous operation), check out this explanation.
If you rewrite the code the way it is going to be executed sequentially then it becomes clearer what's going on.
Original code:
func main() {
a := make(chan bool)
b := make(chan int64)
go func(a chan bool, b chan int64) {
fmt.Println("Here1")
a <- true
b <- 12
} (a,b)
fmt.Println("Here2")
fmt.Println(fmt.Sprintf("%d", <-b))
fmt.Println(fmt.Sprintf("%v", <-a))
}
Close representation of sequential execution of the same code:
a := make(chan bool)
b := make(chan int64)
fmt.Println("Here2") // Prints
// Pass control to new goroutine
fmt.Println("Here1")
a <- true // Write to channel a and block goroutine here and pass control to main
fmt.Println(fmt.Sprintf("%d", <-b)) // Tries to read from b but nothing has been written to it so blocks. At this point all your goroutines are blocked hence the deadlock.
fmt.Println(fmt.Sprintf("%v", <-a)) // doesn't even reach here.
b <- 12
}

Golang: cannot send on channel

Why is it not sending on the channel and blocking the execution? How can I make this constellation work so that I can send a signal into MoneyDive() and continue execution?
package main
import (
"fmt"
)
type Quack func(ch chan bool)
type DagobertDuck struct {
quack Quack
}
func (self *DagobertDuck) MoneyDive() {
ch := make(chan bool)
self.quack(ch)
b := <-ch
if b {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
func mockQuack(ch chan bool) {
fmt.Println("mockQuack start")
ch <- true
fmt.Println("mockQuack done")
}
func main() {
dd := DagobertDuck{quack: mockQuack}
dd.MoneyDive()
}
https://play.golang.org/p/1omlb7u6-A
Because you have an unbuffered channel, and you can only send a value on an unbuffered channel without blocking if there is another goroutine which is ready to receive from it.
Since you only have 1 goroutine, it gets blocked. Solution is simple: launch your Quack.quack() method in a new goroutine:
go self.quack(ch)
Then the output (try it on the Go Playground):
mockQuack start
mockQuack done
true
Another option is to not launch a new goroutine but make a buffered channel, so it can hold some values without any receivers ready to receive from it:
ch := make(chan bool, 1) // buffered channel, buffer for 1 value
This creates a channel which is capable of "storing" one value without any receivers ready to receive it. A second send on the channel would also block, unless the value is received from it first (or a receiver ready to receive a value from it).
Try this buffered channel version on the Go Playground.
Relevant section from the spec: Send statements:
Both the channel and the value expression are evaluated before communication begins. Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready. A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic. A send on a nil channel blocks forever.
Notes:
Based on the received value you print true or false. This can be done with a single line, without the if statement:
fmt.Println(b)
You can even get rid of the b local variable, and print the received value right away:
fmt.Println(<-ch)
Also I assume you used channels because you wanted to play with them, but in your case mockQuack() could simply return the bool value, without the use of channels.

Can't get concurrency to work as I expect it

I am trying to understand why my code does not work, so I managed to recreate the problem in a simpler example.
I expected this code to output the string "broadcasted", but it does not output anything.
package main
import (
"fmt"
"time"
)
type hub struct {
handle chan []byte
broadcast chan []byte
}
func (h *hub) init() {
for {
select {
case m := <-h.handle:
handler(m)
case _ = <-h.broadcast:
fmt.Println("broadcasted")
}
}
}
var socketHub = hub{
handle: make(chan []byte),
broadcast: make(chan []byte),
}
func main() {
go socketHub.init()
m := []byte("this is the message")
socketHub.handle <- m
time.Sleep(time.Second * 2)
}
func handler(m []byte) {
// Do some stuff.
socketHub.broadcast <- m
}
Why does this not work?
Your broadcast channel is unbuffered. This means that:
You send the message to the handle channel: main goroutine blocks until...
In the goroutine, the select case gets the message...
And calls (in the goroutine) handler, which sends the message to the broadcast channel, blocking until...
And that's it: your child goroutine is blocking, waiting on itself to pick the message. Concurrently, your main goroutine sleeps, then reaches the end of main, exits and terminates the program.
You can 'solve' it in several ways:
Make your broadcast channel buffered: this way, goroutine sends the message, which succeeds immediately, and returns in the for loop, selecting it back and printing as intended
Make your send a (new) goroutine, either in handler, or by calling go handler(m) in your loop
Have two different goroutines listen to handler and broadcast
Which one you choose depends on the exact problem you try to solve: in this tiny example, it's hard to find a 'best' one.

How to send and receive values to/from the channel within the same loop?

Here is an example:
func main() {
c := make(chan int)
i := 0
go goroutine(c)
c <- i
time.Sleep(10 * time.Second)
}
func goroutine(c chan int) {
for {
num := <- c
fmt.Println(num)
num++
time.Sleep(1 * time.Second)
c <- num
}
}
What I'm trying to do inside of goroutine is to receive number from channel, print it, increment and after one second send it back to the channel. After this I want to repeat the action.
But as a result, operation is only done once.
Output:
0
Am I doing something wrong?
By default the goroutine communication is synchronous and unbuffered: sends do not complete until there is a receiver to accept the value. There must be a receiver ready to receive data from the channel and then the sender can hand it over directly to the receiver.
So channel send/receive operations block until the other side is ready:
1. A send operation on a channel blocks until a receiver is available for the same channel: if there’s no recipient for the value on ch, no other value can be put in the channel. And the other way around: no new value can be sent in ch when the channel is not empty! So the send operation will wait until ch becomes available again.
2. A receive operation for a channel blocks until a sender is available for the same channel: if there is no value in the channel, the receiver blocks.
This is illustrated in the below example:
package main
import "fmt"
func main() {
ch1 := make(chan int)
go pump(ch1) // pump hangs
fmt.Println(<-ch1) // prints only 0
}
func pump(ch chan int) {
for i:= 0; ; i++ {
ch <- i
}
}
Because there is no receiver the goroutine hangs and print only the first number.
To workaround this we need to define a new goroutine which reads from the channel in an infinite loop.
func receive(ch chan int) {
for {
fmt.Println(<- ch)
}
}
Then in main():
func main() {
ch := make(chan int)
go pump(ch)
go receive(ch)
}
Go Playground
You create an unbuffered channel c with
c := make(chan int)
On unbuffered channels, operations are symmentrical, i.e. every send on a channel needs exactly one receive, every receive needs exactly one send. You send your i into the channel, the goroutine receives it into num. After that, the goroutine sends the incremented num into the channel, but nobody is there to receive it.
In short: The statement
c <- num
will block.
You could use a 1-buffered channel, that should work.
There is another problem with your code that you solved by waiting ten seconds in main: You don't know when your goroutine finishes. Typically, a sync.WaitGroup is used in these situations. But: Your goroutine does not finish. It's idiomatic to introduce a chan struct{} that you close in your main goroutine after a while and select over both channels in the worker goroutine.
You use unbuffered channel, so your goroutine hang on c <- num
You should use buffered channel, like this: c := make(chan int, 1)
Try it on the Go playground

Resources