From: "rkh (Konstantin Haase) via ruby-core" Date: 2026-07-29T08:50:34+00:00 Subject: [ruby-core:126189] [Ruby Bug#22211] Fiber::Scheduler stalls on Ractor::Port.receive Issue #22211 has been updated by rkh (Konstantin Haase). Interesting! I've been playing with workarounds in Ruby-land where I have up to one extra thread per Ractor, which executes `Ractor.select(coordinating_port, *ports)` with a backlog of waiting fibers per port, and then uses a `ConditionalVaribale`/`Mutext` to actually block the fiber. Not trivial and needs a wrapper around `Port.receive` and `Ractor.select`, but better than doing `Thread.new { port.receive }.value`. The nice thing is that the thread can be launched lazily, since `receive` can check whether it is being called from within a non-blocking fiber. Also, I understand if this isn't a priority now, but generally think that at some point this would be great to address. ---------------------------------------- Bug #22211: Fiber::Scheduler stalls on Ractor::Port.receive https://bugs.ruby-lang.org/issues/22211#change-118265 * Author: rkh (Konstantin Haase) * Status: Open * Assignee: ractor * ruby -v: ruby 4.1.0dev (2026-07-24T12:18:38Z master 180b664bd9) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- Quickly upfront: This might be a duplicate of #20276, and let me know if the official solution is still to launch a thread for each port so it can block (or add some more complex thread orchestration). Anyway, I think `Ractor::Port#receive` should call `Fiber::Scheduler#block`/`Fiber::Scheduler#unblock` if a scheduler is set (or some other hook), the way that `Thread::Queue` does. Instead, it completely blocks the scheduler. Example to reproduce: ``` ruby Warning[:experimental] = false class Scheduler def initialize @fiber = Fiber.current @blocked = Set.new @ready = Queue.new end def scheduler_close @ready.pop.transfer while @blocked.any? end def block(blocker, timeout = nil) raise NotImplementedError, "block: timeout not supported" if timeout @blocked << Fiber.current @fiber.transfer ensure @blocked.delete(Fiber.current) end def unblock(blocker, fiber) = @ready << fiber def io_write(io, buffer, offset, length) = Thread.new { buffer.write(io, offset, length) }.value def fiber(&) = Fiber.new(blocking: false, &).tap(&:transfer) def respond_to_missing?(...) = true def method_missing(method, ...) = raise NotImplementedError, "#{method}: not implemented" end Fiber.set_scheduler(Scheduler.new) # This would work: # # queue = Queue.new # Fiber.schedule { puts queue.pop } # queue.push "Hello from Queue!" port = Ractor::Port.new Fiber.schedule { puts port.receive } port.send("Hello from Ractor::Port!") # never gets here ``` -- 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/