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=-3.8 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY shortcircuit=no autolearn=ham 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 A09B11F66F for ; Thu, 29 Oct 2020 19:53:59 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id D33AD120BA5; Fri, 30 Oct 2020 04:53:18 +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 7C40C120BA3 for ; Fri, 30 Oct 2020 04:53:16 +0900 (JST) Received: by filterdrecv-p3iad2-canary-5579457694-fp5cc with SMTP id filterdrecv-p3iad2-canary-5579457694-fp5cc-18-5F9B1DD1-76 2020-10-29 19:53:53.982440548 +0000 UTC m=+514743.954042712 Received: from herokuapp.com (unknown) by ismtpd0031p1iad2.sendgrid.net (SG) with ESMTP id I4RkLLqZQMm9qZnB-hobFA for ; Thu, 29 Oct 2020 19:53:53.930 +0000 (UTC) Date: Thu, 29 Oct 2020 19:53:53 +0000 (UTC) From: merch-redmine@jeremyevans.net Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 76531 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 17273 X-Redmine-Issue-Author: ko1 X-Redmine-Sender: jeremyevans0 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?RVE3t853K5scBhbmJHUzZTFFeVC=2FZSUmHZ0Dc+26wcEi2CTgsF1oz0wTSSxGGN?= =?us-ascii?Q?BIKJJsuIblgkMt2=2FQlgMf9c64iuzEbVHLTA4K2W?= =?us-ascii?Q?UR5csmTUEs0xnVXSw2jP3ooQNNUFItKm9LbP1j6?= =?us-ascii?Q?1NPgKQQq=2Fj5g+Q325D4U2tcX+G8dCUm21fGuGto?= =?us-ascii?Q?LbiEfoAImerIlTPMfcAZd6JRIJUrNVRDNwfhGNl?= =?us-ascii?Q?pK6BIVeKtEhzSeRSnhjY3o9zU1hRwesYPuxe=2FW?= To: ruby-core@ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== X-ML-Name: ruby-core X-Mail-Count: 100659 Subject: [ruby-core:100659] [Ruby master Feature#17273] shareable_constant_value pragma 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 #17273 has been updated by jeremyevans0 (Jeremy Evans). Eregon (Benoit Daloze) wrote in #note-16: > Eregon (Benoit Daloze) wrote in #note-14: > > Of course, no gems should directly mutate constants of another gem. > > I missed that the gem doesn't need to mutate `A` to break `OtherLib`. > I guess it's relatively rare that a gem would (intentionally) expose a non-frozen constant as part of its API, and that the gem relies on being able to mutate it. I agree that it's relatively rare and not a good idea. However, Sequel does this (Sequel::DATABASES). It's been around since 2008 and many external users rely on reading from it, and I haven't wanted to break backwards compatibility to remove it. Internal access is always protected by a mutex for thread-safety, and it only gets mutated for new or removed database connections, so it isn't a problem in practice. ---------------------------------------- Feature #17273: shareable_constant_value pragma https://bugs.ruby-lang.org/issues/17273#change-88299 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal ---------------------------------------- This proposal is to introduce `# shareable_constant_value: true` pragma to make constant values shareable objects. With this pragma, you don't need to add `freeze` to access from non-main ractors. ```ruby # shareable_constant_value: true A = [1, [2, [3, 4]]] H = {a: "a"} Ractor.new do p A p H end.take ``` ## Background Now, we can not access constants which contains a unshareable object from the non-main Ractor. ```ruby A = [1, [2, [3, 4]]] H = {a: "a"} Ractor.new do p A #=> can not access non-sharable objects in constant Object::A by non-main Ractor. (NameError) p H end.take ``` If we know we don't modify `A` and `H` is frozen object, we can freeze them, and other ractors can access them. ```ruby A = [1, [2, [3, 4].freeze].freeze].freeze H = {a: "a".freeze}.freeze Ractor.new do p A #=> [1, [2, [3, 4]]] p H #=> {:a=>"a"} end.take ``` Adding nesting data structure, we need many `.freeze` method. Recently, I added `Ractor.make_shareable(obj)` makes `obj` shareable with freezing objects deeply (see [Feature #17274]). We only need to introduce this method for each constant. ```ruby A = Ractor.make_shareable( [1, [2, [3, 4]]] ) H = Ractor.make_shareable( {a: "a"} ) Ractor.new do p A #=> [1, [2, [3, 4]]] p H #=> {:a=>"a"} end.take ``` However, if we have 100 constants, it is troublesome. ## Proposal With `# shareable_constant_value: true`, you can specify all constants are shareable. ```ruby # shareable_constant_value: true A = [1, [2, [3, 4]]] # compiled with: A = Ractor.make_shareable( [1, [2, [3, 4]]] ) H = {a: "a"} # compiled with: H = Ractor.make_shareable( {a: "a"} ) Ractor.new do p A p H end.take ``` (Strictly speaking, don't call `Ractor.make_shareable`, but apply same effect. This means rewriting `Ractor.make_shareable` doesn't affect this behavior) You can specify `# shareable_constant_value: false` in the middle of the place. ```ruby # shareable_constant_value: true S1 = 'str' # p S1.frozen? #=> true # shareable_constant_value: false S2 = 'str' # p S2.frozen? #=> false ``` The effect of this pragma is closed to the scope. ```ruby class C # shareable_constant_value: true A = 'str' p A.frozen? #=> true 1.times do # shareable_constant_value: false B = 'str' p B.frozen? #=> false end end X = 'str' p X.frozen? #=> false ``` `Ractor.make_shareable(obj)` doesn't affect anything to shareable objects. ```ruby # shareable_constant_value: true class C; end D = C p D.frozen? #=> false ``` Some objects can not become shareable objects, so it raises an exception: ```ruby # shareable_constant_value: true T = Thread.new{} #=> `make_shareable': can not make shareable object for # (Ractor::Error) ``` ## Implementation https://github.com/ruby/ruby/pull/3681/files -- https://bugs.ruby-lang.org/