Skip to content
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

Channel.select bug when closing channel #8231

Open
carlhoerberg opened this issue Sep 25, 2019 · 6 comments
Open

Channel.select bug when closing channel #8231

carlhoerberg opened this issue Sep 25, 2019 · 6 comments

Comments

@carlhoerberg
Copy link
Contributor

The following code:

ch = Channel(Nil).new

spawn do
  loop do
    _, _ = Channel.select(ch.receive_select_action)
    puts "got it"
  end
end

ch.send nil
ch.close
sleep

results in:

 ✗ crystal run close.cr
got it
Unhandled exception in spawn: BUG: Fiber was awaken from select but no action was activated (Exception)
  from /usr/local/Cellar/crystal/0.31.0/src/channel.cr:321:5 in 'select'
  from /usr/local/Cellar/crystal/0.31.0/src/channel.cr:278:3 in 'select'
  from /usr/local/Cellar/crystal/0.31.0/src/channel.cr:275:5 in 'select'
  from close.cr:5:12 in '->'
  from /usr/local/Cellar/crystal/0.31.0/src/fiber.cr:255:3 in 'run'
  from /usr/local/Cellar/crystal/0.31.0/src/fiber.cr:48:34 in '->'
@asterite
Copy link
Member

Also:

ch = Channel(Nil).new

spawn do
  loop do
    select
    when ch.receive
      puts "got it"
    end
  end
end

ch.send nil
ch.close
sleep

(it's the same, but please try to use "public" API when reporting these bugs)

@carlhoerberg
Copy link
Contributor Author

carlhoerberg commented Sep 25, 2019

sure, but i don't think select when is documented anywhere, can't find it here:
https://crystal-lang.org/api/0.31.0/
or here:
https://crystal-lang.org/reference/

@asterite
Copy link
Member

Yeah, unfortunately it's not documented because the entire concurrency thing is still being revised and implemented. But select is the public API, similar to the one in Go. The others are not (they are implementation details).

I just wanted to show that using public API it breaks so it's a legit bug, and not something that you can only cause by using internal API.

@bcardiff
Copy link
Member

@carlhoerberg In #8243 put some explanation for the alternatives syntax and behavior.

If you want to have and "infinite" loop that will exit gracefully (for example upon a Ctrl+C) something like the following might work for you.

ch = Channel(Nil).new
exit1 = Channel(Nil).new
exit2 = Channel(Nil).new

Signal::INT.trap do
  exit1.send nil
end

spawn do
  i = 0
  run = true
  while run
    i = (i + 1) % 1_000
    select
    when ch.receive
      puts "got it"
    when exit1.receive
      run = false
    else
      if i == 0
        print "."
        Fiber.yield
      end
    end
  end

  puts "Exiting loop"
  exit2.send nil
end

ch.send nil
ch.close
exit2.receive

puts "Bye!"

So, the issue in the opening description will still raise an exception in the mentioned PR. Yet, a different one.

@RX14
Copy link
Contributor

RX14 commented Sep 30, 2019

@asterite there needs to be a public API for selecting over dynamically generated groups of fibers. To me Channel.select should be public API since there's no other way to easily achieve what you can do by passing a non-constant array to it.

@asterite
Copy link
Member

Yes, but you need to pass a list of channels, not some internal structures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants