From d82f9d609285ce6d8fbe1e2aaebcb0ac04e4398e Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Wed, 8 Jul 2026 17:30:05 +0200 Subject: [PATCH] Allow to use Concurrent::Map#compute_if_absent in a Ractor - ### Problem It is currently not possible to use the `compute_if_absent` method in a Ractor because there is a NULL object used solely as a sentinel that isn't shareable. ```ruby Ractor.new do map = Concurrent::Map.new map.compute_if_absent(3) do |map, key| end end # Ractor::IsolationError: # can not access non-shareable objects in constant Concurrent::NULL by non-main ractor. ``` ### Solution I'd like to simply freeze the object being hold in the `NULL` constant. --- lib/concurrent-ruby/concurrent/constants.rb | 2 +- spec/concurrent/map_spec.rb | 22 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/concurrent-ruby/concurrent/constants.rb b/lib/concurrent-ruby/concurrent/constants.rb index 676c2afb9..38e01e6b9 100644 --- a/lib/concurrent-ruby/concurrent/constants.rb +++ b/lib/concurrent-ruby/concurrent/constants.rb @@ -3,6 +3,6 @@ module Concurrent # Various classes within allows for +nil+ values to be stored, # so a special +NULL+ token is required to indicate the "nil-ness". # @!visibility private - NULL = ::Object.new + NULL = ::Object.new.freeze end diff --git a/spec/concurrent/map_spec.rb b/spec/concurrent/map_spec.rb index 4f1ff2bff..54c57b7cb 100644 --- a/spec/concurrent/map_spec.rb +++ b/spec/concurrent/map_spec.rb @@ -70,6 +70,28 @@ module Concurrent expect(map.keys).to eq [3] end + if defined?(Ractor) + it 'works in a Ractor' do + ractor = Ractor.new do + inner_map = Concurrent::Map.new do |map, key| + map.compute_if_absent(key) { key * 2 } + end + + inner_map[3] + inner_map + end + + map = if ractor.respond_to?(:take) + ractor.take + else + ractor.value # Ruby 4.0 + end + + expect(map[3]).to eq 6 + expect(map.keys).to eq [3] + end + end + it 'common' do with_or_without_default_proc do expect_size_change(3) do