From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-2.7 required=3.0 tests=AWL,BAYES_00, DKIM_ADSP_CUSTOM_MED,FORGED_GMAIL_RCVD,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 954B91F4B4 for ; Sun, 13 Sep 2020 10:01:56 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 49529120A2C; Sun, 13 Sep 2020 19:01:20 +0900 (JST) Received: from xtrwkhkc.outbound-mail.sendgrid.net (xtrwkhkc.outbound-mail.sendgrid.net [167.89.16.28]) by neon.ruby-lang.org (Postfix) with ESMTPS id 61841120A29 for ; Sun, 13 Sep 2020 19:01:17 +0900 (JST) Received: by filterdrecv-p3mdw1-756b745b58-44t8t with SMTP id filterdrecv-p3mdw1-756b745b58-44t8t-18-5F5DEE07-3A 2020-09-13 10:01:43.388693362 +0000 UTC m=+226995.841317450 Received: from herokuapp.com (unknown) by ismtpd0029p1iad2.sendgrid.net (SG) with ESMTP id tAa7bwpwSBiVis2Jv5B_uw for ; Sun, 13 Sep 2020 10:01:43.335 +0000 (UTC) Date: Sun, 13 Sep 2020 10:01:43 +0000 (UTC) From: eregontp@gmail.com Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 75842 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Bug X-Redmine-Issue-Id: 17159 X-Redmine-Issue-Author: ko1 X-Redmine-Sender: Eregon X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: =?us-ascii?Q?KippOI8ZHtTweq7XfQzW93937kJ4QNWwSBuHnaMEcr0JzdaaBVUAgX+KX+LhDv?= =?us-ascii?Q?bsFSZRxjCTlQs790PpFRm9Rvge5O8iV1OAmEWSp?= =?us-ascii?Q?=2FB2tSZvZZQrNxXmDVS0HCjRgEPBkCIkZkF6wNXw?= =?us-ascii?Q?qJaWZB1esu2JBW2pC6LilNlTxNJVWhjHQHtt9s+?= =?us-ascii?Q?1Cp2yPqzVHJPpaaB570+JSw168IYnCd58X74BPC?= =?us-ascii?Q?SNcIp3m1Ff5FObJ0M=3D?= To: ruby-core@ruby-lang.org X-ML-Name: ruby-core X-Mail-Count: 100002 Subject: [ruby-core:100002] [Ruby master Bug#17159] extend `define_method` for Ractor X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #17159 has been updated by Eregon (Benoit Daloze). Copying captured variables seems a nice feature to have, also for optimizations. In fact, this already exists in TruffleRuby internally: https://github.com/oracle/truffleruby/blob/574d6bd2425caa856707ffd713fdb8ffc87be1e1/src/main/java/org/truffleruby/extra/TruffleGraalNodes.java#L113-L120 It seems known as `fixTemps` in Smalltalk. So I'd suggest to add this as a keyword argument, not sure about the name but `ractorize` doesn't explain what it does. `copy_captured_locals: true`, `copy_captured_variables: true`, `capture_scope: false` maybe? This can also be useful on `Proc` in general, so it might be better to have a method on `Proc` for that. Having it as a keyword argument for `define_method` as a shortcut seems good, since on the implementation side it's useful to combine both operations into one to avoid intermediate Procs, bytecode, etc. For Ractor there is a big limitation though: only shareable objects can be copied that way. Otherwise it would lead to shared state and segv. ---------------------------------------- Bug #17159: extend `define_method` for Ractor https://bugs.ruby-lang.org/issues/17159#change-87549 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN ---------------------------------------- Ractor prohibits to use non-isolated Procs. Non-isolated example is here: ``` s = "foo" pr = Proc.new{ p s } ``` This Proc pr can not be shared with multi-ractors because outer variable `s` can contain an unshareable object. Also outer binding is mutable object so it can lead race condition. Because of these reasons, `define_method` are also problem on multi-Ractor program. (current implementation allows it just because check is not implemented, and it leads BUG). I think there are several patterns `define_method` are used. (1) want to choose variable method names ```ruby name = ... define_method(name){ nil } ``` (2) want to embed variables to the code ```ruby 10.times{|i| define_method("foo{i}"){ i } } ``` (3) want to use global state by local variables ```ruby cnt = 0 define_method("inc"){ cnt += 1 } ``` (4) others I can't imagine ---- (1) is easy. `define_method(name, &Proc{nil}.isoplate)` will be allowed on multi-ractors. (3) is not allowed because it introduces data races/race conditions. For 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 and it should be saved. To help (2) pattern, the easiest way is to use eval. ```ruby 10.times{|i| eval("def foo#{i} #{i}; end") } ``` However, eval has several issues (it has huge freedom to explode the program, editor's syntax highlighting and so on). Another approach is embed the current value to the code, like 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 difference between current Proc semantics. Another idea is to specify embedding value like 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, 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 -- https://bugs.ruby-lang.org/