From c4f084cca2793bb3ad8d94ae549cf413021f165a Mon Sep 17 00:00:00 2001 From: Shannon Skipper Date: Sun, 15 Mar 2026 15:17:37 -0700 Subject: [PATCH] Fix a `RactorLocalSingleton` lazy mutex race `RactorLocalSingleton` lazily creates the mutex that guards instance creation, but that lazy creation isn't itself guarded. Multiple threads can each see `nil`, create their own mutex and synchronize on different locks. This switches to using `Ractor.store_if_absent`, which makes the separate mutex and double-checked locking unnecessary. `Ractor.store_if_absent` had not been introduced yet when this code was initially written. --- lib/singleton.rb | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/singleton.rb b/lib/singleton.rb index 74aec89..556cd0b 100644 --- a/lib/singleton.rb +++ b/lib/singleton.rb @@ -203,13 +203,7 @@ module RactorLocalSingleton # :nodoc: module RactorLocalSingletonClassMethods # :nodoc: include Singleton::SingletonClassMethods def instance - set_mutex(Thread::Mutex.new) if Ractor.current[mutex_key].nil? - return Ractor.current[instance_key] if Ractor.current[instance_key] - Ractor.current[mutex_key].synchronize { - return Ractor.current[instance_key] if Ractor.current[instance_key] - set_instance(new()) - } - Ractor.current[instance_key] + Ractor.store_if_absent(instance_key) { new } end private @@ -217,18 +211,6 @@ def instance def instance_key :"__RactorLocalSingleton_instance_with_class_id_#{object_id}__" end - - def mutex_key - :"__RactorLocalSingleton_mutex_with_class_id_#{object_id}__" - end - - def set_instance(val) - Ractor.current[instance_key] = val - end - - def set_mutex(val) - Ractor.current[mutex_key] = val - end end def self.module_with_class_methods