Project

General

Profile

Actions

Bug #21398

open

Ractor.select hangs when multiple threads submit heavy jobs concurrently

Added by arino.tamada (有乃 玉田) 2 days ago. Updated 1 day ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin23]
[ruby-core:122417]

Description

When multiple threads run heavy Ractor-based jobs at the same time, Ractor.select(*workers) can hang indefinitely without any crash, exception, or deadlock being reported.

This issue does not occur when only one thread is running similar jobs — the hang happens only when four or more threads submit large jobs concurrently to a shared Ractor pipeline.

Steps to Reproduce

Run the script below in Ruby 3.4.2 or later.

In some runs, one or more threads will hang forever at Ractor.select.

The issue reproduces more easily on machines with multiple cores and sufficient memory.

Expected Behavior

All calls to Ractor.select(*workers) should return when any worker Ractor yields a result. All jobs should complete.

Actual Behavior

  • In some runs (especially with multiple threads), Ractor.select hangs and never returns.

  • There is no crash or exception; the process remains alive but blocked.

  • All Ractors and threads are created and started correctly.

  • This behavior does not occur when only one thread performs the same job sequentially.

  • It appears that Ractor.select does not always behave reliably under concurrent multithreaded usage with heavy Ractor pipelines.

Reproducible Example:

THREADS = 4
JOBS_PER_THREAD = 10
ARRAY_SIZE = 10_000

def ractor_job(job_count, array_size)
  pipe = Ractor.new do
    loop { Ractor.yield Ractor.receive }
  end

  workers = (1..4).map do
    Ractor.new(pipe) do |pipe|
      while job = pipe.take
        result = job.map { |x| x * 2 }.sum
        Ractor.yield result
      end
    end
  end

  jobs = Array.new(job_count) { Array.new(array_size) { rand(1000) } }
  jobs.each { |job| pipe.send(job) }

  results = []
  jobs.size.times do
    puts "Waiting for results..."
    _ractor, result = Ractor.select(*workers)
    puts "Received result: #{result}"
    results << result
  end
  results
end

threads = []
THREADS.times do
  threads << Thread.new do
    ractor_job(JOBS_PER_THREAD, ARRAY_SIZE)
  end
end

threads.each(&:join)
puts "All threads finished."

Updated by luke-gru (Luke Gruber) 2 days ago · Edited

Thank you for the report.

Is it possible for you to try to install ruby-head (3.5.0dev) and try this script with the recent changes to ractors (ractor ports)? I'm having trouble reproducing it on my end on ruby-head.

Here's the script with modifications for working with ports:

THREADS = 4
JOBS_PER_THREAD = 10
ARRAY_SIZE = 10_000

def ractor_job(job_count, array_size)
  port = Ractor::Port.new

  workers = (1..4).map do |i|
    Ractor.new(port) do |job_port|
      while job = Ractor.receive
        result = job.map { |x| x * 2 }.sum
        job_port.send result
      end
    end
  end

  jobs = Array.new(job_count) { Array.new(array_size) { rand(1000) } }
  jobs.each_with_index do |job, i|
    w_idx = i % 4
    workers[w_idx].send(job)
  end

  results = []
  jobs.size.times do
    puts "Waiting for results..."
    _ractor, result = Ractor.select(port)
    puts "Received result: #{result}"
    results << result
  end
  results
end

threads = []
THREADS.times do
  threads << Thread.new do
    ractor_job(JOBS_PER_THREAD, ARRAY_SIZE)
  end
end

threads.each(&:join)
puts "All threads finished."

If you're unfamiliar with ractor ports, here's the ticket for the feature: https://bugs.ruby-lang.org/issues/21262

Updated by arino.tamada (有乃 玉田) 1 day ago

Thank you for your response.

I have tried running the script with Ruby 3.5.0-dev (ruby-head) and using Ractor::Port as in your example.
I can confirm that the issue does not reproduce in my environment with these recent changes.
The script completes successfully and does not hang at Ractor.select.

Thank you for your support and the improvements to Ractor!

Actions

Also available in: Atom PDF

Like0
Like0Like0