Feature #17159
Updated by shyouhei (Shyouhei Urabe) over 4 years 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