From: sdwolfz via ruby-core Date: 2025-10-18T13:34:25+00:00 Subject: [ruby-core:123495] [Ruby Feature#19326] Please add a better API for passing a Proc to a Ractor Issue #19326 has been updated by sdwolfz (Codru�� Gu��oi). byroot (Jean Boussier) wrote in #note-13: > I think we can close now? ```ruby require 'pp' class Worker def initialize(*args, &block) @args = args @block = Ractor.shareable_proc(&block) end def run(count) @workers = Array.new(count) do |i| Ractor.new(i, @args, @block) { |id, args, blk| blk.call(id, *args) } end end def send(index, msg) @workers[index].send(msg) end end printer = Worker.new do loop do msg = Ractor.receive break if msg == :shutdown pp(msg) end end printer.run(1) worker = Worker.new(printer) do |id, printer| loop do msg = Ractor.receive break if msg == :shutdown printer.send(0, {worker: id, msg: msg}) end printer.send(0, "worker #{id} done") end worker.run(3) 10.times { |n| worker.send(n % 3, "message #{n}") } sleep 1 ``` Gives: ``` {worker: 1, msg: "message 1"} {worker: 1, msg: "message 4"} {worker: 1, msg: "message 7"} {worker: 0, msg: "message 0"} {worker: 0, msg: "message 3"} {worker: 0, msg: "message 6"} {worker: 0, msg: "message 9"} {worker: 2, msg: "message 2"} {worker: 2, msg: "message 5"} {worker: 2, msg: "message 8"} ``` Yes it seems to be working properly. ---------------------------------------- Feature #19326: Please add a better API for passing a Proc to a Ractor https://bugs.ruby-lang.org/issues/19326#change-114867 * Author: sdwolfz (Codru�� Gu��oi) * Status: Closed * Assignee: ko1 (Koichi Sasada) ---------------------------------------- Example 1: ```ruby class Worker def initialize(&block) @block = block end def run Ractor.new(@block, &:call) end end worker = Worker.new { 1 } puts worker.run.take ``` Errors with: ``` :271:in `new': allocator undefined for Proc (TypeError) from scripts/run.rb:9:in `run' from scripts/run.rb:14:in `
' ``` Example 2: ```ruby class Worker def initialize(&block) @block = Ractor.make_shareable(block) end def run Ractor.new(@block, &:call) end end worker = Worker.new { 1 } puts worker.run.take ``` Errors with: ``` :820:in `make_shareable': Proc's self is not shareable: # (Ractor::IsolationError) from scripts/run.rb:5:in `initialize' from scripts/run.rb:13:in `new' from scripts/run.rb:13:in `
' ``` Example 3: ```ruby class Worker def initialize(&block) @block = Ractor.make_shareable(block) end def run Ractor.new(@block, &:call) end end worker = Ractor.current.instance_eval { Worker.new { 1 } } puts worker.run.take ``` Works, but having `Ractor.current.instance_eval` as a wrapper around the block is not ideal, as Ractor is supposed to be only an implementation detail in Worker. I know about https://bugs.ruby-lang.org/issues/18243 and the discussion around `proc.bind(nil)`. That would actually be ideal, as for the purposes if why I want this functionality I don't care what `self` is in a block, and the less it has access to the better. The general idea of Worker is to have a Ractor be able to lazily execute an arbitrary proc. And all the bindings it would need would be passed explicitly, either through `args` in the constructor or through `send`/`receive`, so `self` would really not matter. The benefit: this would make it so concurrent code can be more easily be implemented with Ractors as currently you can execute an arbitrary proc by passing it to a Thread (but you don't get the nice data isolation). -- 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/lists/ruby-core.ml.ruby-lang.org/