Project

General

Profile

Bug #20626

Updated by ko1 (Koichi Sasada) 7 days ago

Return `nil` for `defined?(@ivar)` on some cases with Ractor. 

 ## Background 

 It is not allowed to get instance variables from an object if 

 1. the object is shareable 
 2. and the instance variable refers to an unshareable object 

 like the following code: 

 ```ruby 
 class Ractor 
   def setup_iv 
     @iv = [] 
   end 
   def get_iv = @iv 
 end 

 R = Ractor.new{} 
 R.setup_iv 
 p R.get_iv #=> [] 

 Ractor.new{|;iv| 
   p R.get_iv #=> can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError) 
 }.take 
 ``` 

 I believe nobody use ivars on Ractor objects. However it is used on classes and modules (shareable objects) casually use it. 


 ```ruby 
 class C 
   @iv = [] 
   def self.iv = @iv 
 end 

 p C.iv #=> [] 

 Ractor.new{ 
   p C.iv #=> can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError) 
 }.take 
 ``` 

 Current behavior on `defined?` with such ivars is strange because: 

 * Case1: raising an error 

 ```ruby 
 class C 
   @iv1 = [] 
   def self.defined_iv1 = defined?(@iv1) 
 end 

 Ractor.new{ 
   p C.defined_iv1 
   #=> can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError) 
 }.take 
 ``` 

 * Case2: incorrect result 

 ```ruby 
 class C 
   # @iv2 is not defined 
   def self.defined_iv2 = defined?(@iv2) 
 end 

 Ractor.new{ 
   p C.defined_iv2 #=> "instance-variable" 
 }.take 
 ``` 

 This is because current implementation uses accessing ivars on such cases. It seems a simple bug. 

 ## Proposal 

 In other words, we can't use such ivars on Ractors on Case1. 
 So that returning `nil` on Case1 and Case2 reasonable. 


 ```ruby 
 class C 
   @iv1 = [] 
   def self.defined_iv1 = defined?(@iv1) 

   # @iv2 is not defined 
   def self.defined_iv2 = defined?(@iv2) 

   @iv3 = 42 # refers to shareable object 
   def self.defined_iv2 = defined?(@iv3) 
 end 

 p C.defined_iv1 #=> "instance-variable" 
 p C.defined_iv2 #=> "instance-variable" 

 Ractor.new{ 
   p C.defined_iv1 
    # current:    can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError) 
    # proposed: nil because it is not accessible from here 

   p C.defined_iv2 
    # current: "instance-variable" 
    # proposed: nil 
   
   p C.defined_iv3 #=> "instance-variable" because we can access it 
 }.take 
 ``` 

 ## Usage 

 `FileUtils` uses `defined?(@non_existing_ivar)` technique to configure its behavior so I found the Case2. 
 https://github.com/ruby/fileutils/blob/master/lib/fileutils.rb#L2501 

 ## Implementation 

 https://github.com/ruby/ruby/pull/11141 

 We need to more modification to YJIT. 

Back