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

Also available in: Atom PDF

Like0
Like0Like0