Crystal: Crash in fiber switching with select

Created on 15 Jan 2017  路  7Comments  路  Source: crystal-lang/crystal

ch1 = Channel::Buffered(Int32).new(1)
ch2 = Channel::Buffered(Int32).new(1)
res = [] of Int32

spawn do
  loop do
    select
    when x = ch1.receive
      res << x
    when y = ch2.receive
      res << y
    end
  end
end

spawn do
  3.times do |i|
    select
    when ch1.send(i)
    when ch2.send(i)
    end
  end
end

Fiber.yield
p res
Invalid memory access (signal 11) at address 0x0
[4466037] *CallStack::print_backtrace:Int32 +117
[4444920] __crystal_sigfault_handler +56
[4758788] sigfault_handler +40
[140503573855408] ???
[0] ???
to-research bug topicconcurrency

Most helpful comment

I tried it with #8112 and it works fine 馃帀

All 7 comments

Did some playing around with this today. Doesn't seem to happen if case statement is changed to if, and doesn't seem to happen if case statement is changed to if/elsif (with or without plain else statement).
Fails right after a stack switch, according to gdb. I logged every crystal function, and couldn't get any closer to the exact error location.
I'm trying to get a copy of the generated code to see what the end product of the case statements look like (it appears they're turned into if/elsif), but beyond that, I'm out of ideas.
I'm not sure how helpful any this is, but the issue was marked help-wanted, so figured I'd comment.

i tried to debug this, but its quite hard, something breaks in coroutines switches.

Because there is a same fiber in ch1 and ch2, that could push the same fiber into scheduler twice. If you add such logger, then you will see that.

ch1 = Channel(Int32).new(1)
ch2 = Channel(Int32).new(1)
res = [] of Int32

class Scheduler
  def self.log
    LibC.printf "#{@@runnables}\n"
  end
end

a = spawn do
  loop do
    select
    when x = ch1.receive
      res << x
    when y = ch2.receive
      res << y
    end
    LibC.printf "receive: "
    Scheduler.log
  end
end

b = spawn do
  3.times do |i|
    select
    when ch1.send(i)
    when ch2.send(i)
    end
    LibC.printf "send: "
    Scheduler.log
  end
end

p "receive fiber : #{a}"
p "send fiber : #{b}"

Fiber.yield
p res
"receive fiber : #<Fiber:0x1d37e00>"
"send fiber : #<Fiber:0x1d37d90>"
send: Deque{#<Fiber:0x1d37e00>}
send: Deque{#<Fiber:0x1d37e00>, #<Fiber:0x1d37e00>}
receive: Deque{#<Fiber:0x1d37e00>, #<Fiber:0x1d37d90>}
receive: Deque{#<Fiber:0x1d37e00>, #<Fiber:0x1d37d90>, #<Fiber:0x1d37d90>}
send: Deque{#<Fiber:0x1d37d90>, #<Fiber:0x1d37e00>}
Invalid memory access (signal 11) at address 0x0
[4413221] *CallStack::print_backtrace:Int32 +117
[4391592] __crystal_sigfault_handler +56
[4608565] sigfault_handler +40
[140189768220800] ???
[0] ???

After third sending, the fiber are going to the end and Scheduler will resume the fiber again. That's why it crashed.

triage

This issue appears to persist with 0.23.1 https://carc.in/#/r/2imm

An effort to rewrite the Channel implementation has been undertaken in #3912, and also appears to have not much feedback.

It doesn't segfault anymore, but still crashes at runtime: https://carc.in/#/r/7gtb

Somebody wants to try it against #8112? :)

I tried it with #8112 and it works fine 馃帀

Closed by #8112

Please reopen if the issue remains.

Was this page helpful?
0 / 5 - 0 ratings