[ruby-core:112659] [Ruby master Feature#19472] Ractor::Selector to wait multiple ractors
From:
"ioquatix (Samuel Williams) via ruby-core" <ruby-core@...>
Date:
2023-03-02 07:13:42 UTC
List:
ruby-core #112659
Issue #19472 has been updated by ioquatix (Samuel Williams).
Is it compatible with fiber scheduler?
----------------------------------------
Feature #19472: Ractor::Selector to wait multiple ractors
https://bugs.ruby-lang.org/issues/19472#change-102107
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
* Target version: 3.3
----------------------------------------
This ticket propose `Ractor::Selector` API to wait multiple ractor events.
Now, if we want to wait for taking from r1, r2 and r3, we can use `Ractor.select()` like that.
```ruby
r, v = Ractor.select(r1, r2, r3)
p "taking an object #{v} from #{r}"
```
With proposed `Ractor::Selector` API, we can write the following:
```ruby
selector = Ractor.selector.new(r1, r2) # make a waiting set with r1 and r2
selector.add(r3) # we can add r3 to the waiting set after that.
selector.add(r4)
selector.remove(r4) # we can remove r4 from the waiting set.
r, v = selector.wait
p "taking an object #{v} from #{r}"
```
* `Ractor::Selector.new(*ractors)`: create a selector
* `Ractor::Selector#add(r)`: add `r` to the waiting list
* `Ractor::Selector#remove(r)`: remove `r` from the waiting list
* `Ractor::Selector#clear`: remove all ractors from the waiting list
* `Ractor::Selector#wait`: wait for the ractor events
https://github.com/ruby/ruby/pull/7371/files#diff-2be07f7941fed81f90e2947cdd9a91a5775d0c94335e8332b4805d264380b255R380
The advantages comparing with `Ractor.select` are:
* (1) (API design) We can preset the waiting set before waiting. Providing unified way to manage waiting list seems better.
* (2) (Performance) It is lighter than passing an array object to the `Ractor.select(*rs)` if `rs` is bigger and bigger.
For (2), it is important to supervise thousands of ractors.
`Ractor::Selector#wait` also has additional features:
* `wait(receive: true)` also waits receiving.
* `Ractor.select(*rs, Ractor.current)` does same, but I believe `receive: true` keyword is more direct to understand.
* `wait(yield_value: obj, move: true/false)` also waits yielding.
* Same as `Ractor.select(yield_value: obj, move: true/false)`
* If a ractor `r` is closing, then `#wait` removes `r` automatically.
* If there is no waiting ractors, it raises an exception (now `Ractor::Error` is raised but it should be a better exception class)
With automatic removing, we can write the code to wait n tasks.
```ruby
rs = n.times.map{ Ractor.new{ do_task } }
selector = Ractor::Selector.new(*rs)
loop do
r, v = selector.wait
handle_answers(r, v)
rescue Ractor::Error
p :all_tasks_done
end
```
Without auto removing, we can write the following code.
```ruby
rs = n.times.map{ Ractor.new{ do_task } }
selector = Ractor::Selector.new(*rs)
loop do
r, v = selector.wait
handle_answers(r, v)
rescue Ractor::ClosedError => e
selector.remove e.ractor
rescue Ractor::Error
p :all_tasks_done
end
# or on this case worker ractors only yield one value (at exit) so the following code works as well.
loop do
r, v = selector.wait
handle_answers(r, v)
selector.remove r
rescue Ractor::Error
p :all_tasks_done
end
```
I already merged it but I want to discuss about the spec.
Discussion:
* The name `Selector` is acceptable?
* Auto-removing seems convenient but it can hide the behavior.
* allow auto-removing
* allow auto-removing as configurable option
* per ractor or per selector
* which is default?
* disallow auto-removing
* What happens on no taking ractors
* raise an exception (which exception?)
* return nil simply
maybe and more...
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/