From: "vinistock (Vinicius Stock)" Date: 2021-10-29T13:21:01+00:00 Subject: [ruby-core:105866] [Ruby master Feature#18275] Add an option to define_method to not capture the surrounding environment Issue #18275 has been updated by vinistock (Vinicius Stock). > I can't get your point. > Methods are unrelated to Ractor. If we create a method using `define_method`, it only exists in the Ractor that made the invocation. Trying to invoke that method from a different Ractor throws an error (something like `defined in another Ractor`). This can be limiting for certain types of Ractor applications. Let's continue with the previous example of a test framework. ```ruby class Test def self.test(name, &block) define_method("test_#{name}", &block) end end class MyTest < Test test "that it works" do # ... end end ``` In a scenario like that, all methods defined by the `test` singleton method will be created when loading the test classes, such as `MyTest`. This typically means that the methods will end up being defined in the main Ractor while requiring files. If we try to create a worker pool of Ractors to run the tests, none of the Ractors have access to the methods that were defined invoking `test`. I imagined that the reason for that is because `define_method` captures the surrounding environment. That's why I thought, maybe if we don't capture it we could have the dynamic be accessible to all Ractors. Is there a different reason behind why other Ractors can't invoke methods dynamically defined by another Ractor? ---------------------------------------- Feature #18275: Add an option to define_method to not capture the surrounding environment https://bugs.ruby-lang.org/issues/18275#change-94404 * Author: vinistock (Vinicius Stock) * Status: Open * Priority: Normal ---------------------------------------- Invoking `define_method` will capture the surrounding environment, making sure we have access to anything defined in that surrounding scope. However, that���s not always necessary. There are uses for `define_method` where the surrounding environment is not needed. Always capturing the surrounding environment slows down even the methods that don���t need access to it. Additionally, it prevents methods created using `define_method` to exist in all Ractors in a program. If we could add an option to disable capturing the surrounding environment for `define_method`, we could make it so that it creates the dynamic method in all Ractors. There could also be some performance benefits for the usages that do not need the surrounding environment. By not having to keep references to the surrounding scope, the GC could let go of locals from that environment, which might benefit GC as well. Another option could be to accept the list of locals that the `define_method` invocation will need, as a way of letting go of references that are no longer needed. Examples: ```ruby # Current behavior # # All of the surrounding environment is captured and references are kept for the locals # The method created only exists in the current Ractor, due to possible references to the captured variables some_random_thing = "a" * 10000 some_captured_block = -> { ... } define_method(:my_method, &some_captured_block) ``` ```ruby # Enable/disable all option # # Add an option that allows disabling capturing the surrounding environment completely # The method created exists in all Ractors and none of the references are kept some_random_thing = "a" * 10000 some_captured_block = -> { ... } define_method(:my_method, capture_environment: false, &some_captured_block) ``` ```ruby # Choose variables option # # Add an option that allows indicating which locals are needed for a define_method invocation # The method created exists in all Ractors if no locals are needed # The method is created only in the current Ractor if at least one local is needed # All ���unneeded��� locals are let go some_random_thing = "a" * 10000 # kept because `my_method` needs it another_random_thing = "b" * 10000 # not kept some_captured_block = -> { ... } define_method(:my_method, needs: [:some_random_thing], &some_captured_block) ``` -- https://bugs.ruby-lang.org/ Unsubscribe: