Project

General

Profile

Bug #17159

Updated by shyouhei (Shyouhei Urabe) 6 months ago

Ractor prohibits to use of non-isolated `Proc`s. Procs. 

 Non-isolated example is here: 

 ```ruby ``` 
 s = "foo" 
 pr = Proc.new{ p s } 
 ``` 

 This Proc `pr` pr can not be shared among ractors with multi-ractors because outer variable `s` can contain an unshareable object. Also outer binding is a mutable object. Sharing object so it can lead race conditions. condition. 

 Because of these reasons, `define_method` is are also a problem on a multi-Ractor program. 
 (current implementation allows it just because check is not implemented, and it leads BUG). 


 I think there are several patterns when `define_method` is needed. are used. 

 (1) To want to choose variable method names on-the-fly 

 ```ruby 
 name = ... 
 define_method(name){ nil } 
 ``` 

 (2) To want to embed variables to the code 

 ```ruby 
 10.times{|i| 
   define_method("foo{i}"){ i } 
 } 
 ``` 

 (3) To want to use global state by local variables 

 ```ruby 
 cnt = 0 
 define_method("inc"){ cnt += 1 } 
 ``` 

 (4) Others others I can't imagine 

 ---- 

 (1) is easy. We can allow `define_method(name, &Proc{nil}.isoplate)`. &Proc{nil}.isoplate)` will be allowed on multi-ractors. 

 (3) can never be OK. It is not allowed because it introduces data races/race conditions. For this purpose one example, we need to use shared hash. 

 ```ruby 
 STATE = SharedHash.new(cnt: 0) 
 define_method("inc"){ STATE.transaction{ STATE[:cnt] += 1 }} 
 ``` 

 I think there are many (2) patterns that and it should be saved. 
 To help (2) pattern, the easiest way is to use `eval`. eval. 

 ```ruby 
 10.times{|i| 
   eval("def foo#{i} #{i}; end") 
 } 
 

 ``` 

 However, `eval` eval has several issues (it has huge freedom to explode the program, editor's syntax highlighting and so on). 

 Another approach is to embed the current value to the code, like this: 

 that: 


 ```ruby 
 i = 0 
 define_method("foo", ractorise: true){ i } 
 #=> equivalent to: 
 #     define_method("foo"){ 0 } 
 # so that if outer scope's i changed, not affected. 
 i = 1 
 foo #=> 0 

 s = "" 
 define_method("bar", ractorise: true){ s } 
 #=> equivalent to: 
 #     define_method("bar"){ "" } 
 # so that if outer scope's s or s's value, it doesn't affect 
 s << "x" 
 bar #=> "" 
 ``` 

 However, it is very differenct from difference between current Proc semantics. 

 
 Another idea is to specify embedding value like this: that. 

 ```ruby 
 i = 0 
 define_method("foo", i: i){ i } 
 #=> equivalent to: 
 #     define_method("foo"){ 0 } 
 # so that if outer scope's i changed, not affected. 
 i = 1 
 foo #=> 0 

 s = "" 
 define_method("bar", s: s){ s } 
 #=> equivalent to: 
 #     define_method("bar"){ "" } 
 # so that if outer scope's s or s's value, it doesn't affect 
 s << "x" 
 bar #=> "" 
 ``` 

 `i: i` and `s: s` are redundant. However, however, if there are no outer variable `i` or `s`, the `i` and `s` in blocks are compiled to `send(:i)` or `send(:s)`. But I agree these method invocation should be replaced is another idea. 


 Thoughts? 

 Thanks, 
 Koichi

Back