Need document for how porting IO.select(...) in ruby Crystal

IO.select was removed since 2017 by this PR.

But for users come from ruby, it still not easy to porting exists code into Fiber + Channel, there is still no a detailed document for how to do this, some examples in that PR is only touch the most simple user case.

For me, a user use Crystal for half years, still don’t know how to porting following code to Crystal, showing real-life use cases is often not as easy as comments in the PR.

Basically, this is a example read non-blocked from several ios, for differnt io, we may need handle differently, with a timeout settings.

Thread.new do
  timeout = 30

  loop do
   # try to read from io1 and io_list (io_list is a array)
    io = IO.select([io1] + io_list, nil, nil, timeout)

    run_common_logic

    if io # if no timeout
      io.first.each do |reader|
        if reader == io1
          # logic if select on io1
          read_from_io1
          next
        end

        # logic if select on the elements of io_list (io_list may be empty)
        read_from_elements_of_io_list
      end
    end
  end
end

For above code, there is some concerns need to mention:

  1. the io_list will changes when running, that is why in the above code, io_list is in a loop, we have to handle it when porting.

  2. we have to handle the timeout in IO.select(io_list, nil, nil, timeout)

  3. pay attention to there is a if io condition and next, This may be a complicated place.

Any idea? if this code can porting to Crystal correctly, it will be a perfect example for original PR.

Thanks.


BTW: this post is related to another my old post Porting a really Ruby IO.select use case into Crystal created several month ago, there is still no a working solution since that.

2 Likes

Crystal uses libevent for evented I/O, so it’s already non-blocking, and it can eliminate the need for select in some cases. In other cases, I would recommend spawning a reader fiber and a timeout fiber, then having a common Channel in which the fastest (reader or timeout) fiber sends a message, e.g. the timeout fiber sends a Nil, or the reader fiber sends a String.

Could you please give a example? e.g. combined with above context.

I figure you might get more responses if you described the behaviour you’re looking to implement instead of just pasting some code that implements this in Ruby. The code example is decently complex and hard to reason about without context. You’re forcing anyone willing to help you to dig into this code and try to understand what you even want before they can start helping you with doing this in Crystal.
If you explain your problem in prose, that’s much easier. Existing code can certainly be a helpful addition, but as sole source of knowledge, it’s quite a hurdle.

2 Likes

Yes, probably people confusing by the context, thought the key is not regarding tightly the context, I just want to learn the way to turn above IO.select logic into Crystal, maybe following pseudo-code is better?

Thread.new do
  timeout = 30

  loop do
   # try to read from io1 and io_list (io_list is a array)
    io = IO.select([io1] + io_list, nil, nil, timeout)

    run_common_logic

    if io # if no timeout
      io.first.each do |reader|
        if reader == io1
          # logic if select on io1
          read_from_io1
          next
        end

        # logic if select on the elements of io_list (io_list may be empty)
        read_from_elements_of_io_list
      end
    end
  end
end

@straight-shoota , Hi, i update my original post according to my previous reply, is the example code clear enough now?

Basically, this is a example read non-blocked from several ios, we may need handle differently according different IO, with a timeout settings.

Hi, @ysbaddaden , could you please help on this question?

this issue come from one long living issue for one of my OS project. procodile.cr, although this shards port by me from it ruby version, but, this is a quite complex shards, especially when porting the usage of IO. select, the complexity definitely exceeds the examples in our [github issue example](Remove IO.select by RX14 · Pull Request #4392 · crystal-lang/crystal · GitHub -300141926)

This raises a problem, IO. select has been removed, but there is no good(real use and complex) documentation to explain how to convert it into a Fiber non block implementation

Every once in a while, I am try fixing it myself, but no luck, I notice this feature Added and Remove, you are the living participant, could you please give some clue about how to solve this?

The issue is simple, i want to port this code into it Crystal equivalent.

Thank you.

Nothing more than what has already been said. Ruby’s IO.select (or the underlying select(2) syscall)` uses polling: you build a list of IO and wait until at least one is ready for read or write, then go on to read/write on these IO.

With coroutines (be them goroutines or fibers) you spawn a fiber for each IO, then merely read/write. If any would block, the fiber will be suspended until the IO is ready.

So instead of:

# select requires to collect IOs:
loop do
  ready = IO.select(ios)
  ready.each do |io| 
    io.read(..)
  end
end

You do:

# spawn a fiber when you create an IO (for example)
spawn do
  while io.read?(...)
    # ...
  end
end
2 Likes