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:
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.
we have to handle the timeout in IO.select(io_list, nil, nil, timeout)
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.
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.
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.
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
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.
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