Project

General

Profile

Actions

Feature #19472

open

Ractor::Selector to wait multiple ractors

Added by ko1 (Koichi Sasada) about 1 year ago. Updated 3 months ago.

Status:
Open
Target version:
[ruby-core:112656]

Description

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.

r, v = Ractor.select(r1, r2, r3)
p "taking an object #{v} from #{r}"

With proposed Ractor::Selector API, we can write the following:

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): creates a selector.
  • Ractor::Selector#add(r): adds r to the waiting set.
  • Ractor::Selector#remove(r): removes r from the waiting set.
  • Ractor::Selector#clear: remove all ractors from the waiting set.
  • Ractor::Selector#empty?: returns if the waiting set is empty or not.
  • Ractor::Selector#wait: waits for the ractor events from the waiting set.

https://github.com/ruby/ruby/blob/master/ractor.rb#L380

The advantages comparing with Ractor.select are:

  • (1) (API design) We can preset the waiting set before waiting. Providing unified way to manage a waiting set 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.

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.

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...

Actions #1

Updated by ko1 (Koichi Sasada) about 1 year ago

  • Description updated (diff)

Updated by ioquatix (Samuel Williams) about 1 year ago

Is it compatible with fiber scheduler?

Updated by Eregon (Benoit Daloze) about 1 year ago

ko1 (Koichi Sasada) wrote:

For (2), it is important to supervise thousands of ractors.

That currently (AFAIK) means thousands of OS threads, and that AFAIK results in very bad performance or scheduling from the kernel.
Is there a real use-case for so many Ractors?

Updated by ko1 (Koichi Sasada) about 1 year ago

Eregon (Benoit Daloze) wrote in #note-3:

That currently (AFAIK) means thousands of OS threads, and that AFAIK results in very bad performance or scheduling from the kernel.
Is there a real use-case for so many Ractors?

This is why I'm working on MaNy project (M:N threads).

Actions #5

Updated by ko1 (Koichi Sasada) about 1 year ago

  • Description updated (diff)

Updated by ioquatix (Samuel Williams) about 1 year ago

Is there a real use-case for so many Ractors?

This is why I'm working on MaNy project (M:N threads).

So it sounds like there is no use case?

At most, 2x ractors as the number of CPU cores should be enough to saturate a system, no?

(I'm not against the proposal, it seems pretty reasonable to me).

Actions #7

Updated by ko1 (Koichi Sasada) about 1 year ago

  • Description updated (diff)
Actions #8

Updated by ko1 (Koichi Sasada) 3 months ago

  • Target version changed from 3.3 to 3.4
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like1Like0Like0Like0Like0