-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug in select when one channel closed #8677
Comments
I don't like that there's not a way to do this? Is go really the same? |
The equivalent to go's is to use |
@bcardiff but that would return
|
Right. Using While working on #8304 I recall that we thought/decide that executing a Together with the above, we wanted to unify some behaviors. We have ch = Channel(Int32).new
ch.close
ch.receive # raise: Channel is closed (Channel::ClosedError) and ch.receive? # => nil So on blocking and non-blocking select
when a = ch.receive
a
end # raise: Channel is closed (Channel::ClosedError) select
when a = ch.receive?
a
end # => nil Maybe the behavior of I liked that |
Usage of select is sync state while receive data from multiple channels, its normal that some of them can be already closed. I think it should raise error only when all channels closed. |
Perhaps |
@kostya you mean to raise if all the channels are closed from the beginning and the select is blocking. Plus trigger the What do you suggest for non-blocking select? trigger the (maybe @firejox will want to join the conversation). @RX14 let's focus on semantics first. But I would like to avoid parameters within the |
Comparing things with Go. The following code executes the c1 or c2 branch without guarantee. So an already closed channel triggers the blocking select also in Go. package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan int)
c2 := make(chan int)
close(c1)
go func () {
c2 <- 2
}()
time.Sleep(500 * time.Millisecond) // ensure c2 has a message
go func() {
fmt.Println("a")
select {
case m, x := <-c1:
fmt.Println("c1 ", m, x)
case m, x := <-c2:
fmt.Println("c2 ", m, x)
}
fmt.Println("z")
}()
time.Sleep(500 * time.Millisecond) // wait for the select
fmt.Println("done")
} |
@bcardiff the semantics for what i proposed would be that you can specify a parameter which means recieve wouldn't raise when that channel is closed when used in select, unless all channels were closed. |
Here are the relevant tests for Go's select over closed channels: Selects with closed channels behave like ordinary operations: receive doesn't block while send panics; Channels thus behave the same whatever if they're running inside a Yet, if multiple channels are ready (before blocking), select will choose a channel to read/write from/to at random, which may allow to read from channel A while channel B is closed if running inside a loop (with a |
I agree with @bcardiff: there shouldn't be any parameter to |
I was going to propose to allow It sounded weird but... https://dave.cheney.net/2013/04/30/curious-channels (Jump to "A nil channel always blocks"). Ideas? |
Alternatives:
ch1 = Channel(Int32).new
ch2 = Channel(Int32).new
spawn do
sleep 0.1
ch1.send(1)
ch1.close
end
spawn do
4.times do
sleep 0.2
ch2.send(2)
end
ch2.close
end
nil_channel = Channel(Int32).new
while ch1 != nil_channel || ch2 != nil_channel
select
when v = ch1.receive?
if v
puts "ch1 - #{v}"
else
ch1 = nil_channel
puts "ch1 closed"
end
when v = ch2.receive?
if v
puts "ch2 - #{v}"
else
ch2 = nil_channel
puts "ch2 closed"
end
end
end |
I think the ignoring behavior is a bug in 0.31.1. Closing channel during Besides, I think the new feature is unneeded. We can just maintain available channels on each |
Ok. Looks like need to rethink how to use 'close' on channels. And actually not use it in my cases. I used it like in sockets, just close when it not needed (in sockets to free descriptors). If i remove close from example it works as expected. So if i would create millions of short-living channels (for timeouts for example), and not closing them, and its ok for GC, so i guess i not going to use 'close'. |
We are all learning and trying to get better here :-) We can create a NilChannel that can be probably even a struct, not inheriting Channel, and implement the But also you can have a single (per each type) of the current channels also. |
this example work fine in 0.31.1 and failed in 0.32.1:
after reading this https://gist.github.com/bcardiff/289953a80eb3a0512a2a2f8c8dfeb1db, i tried receive?, but it just return nil forever. I think this error should only raise when all channels closed.
The text was updated successfully, but these errors were encountered: