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 9E7BA1F4B4 for ; Wed, 21 Oct 2020 19:39:41 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 0F663120B0A; Thu, 22 Oct 2020 04:38:51 +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 A5171120B06 for ; Thu, 22 Oct 2020 04:38:48 +0900 (JST) Received: by filterdrecv-p3mdw1-canary-7b7d5ffdc9-w9dc4 with SMTP id filterdrecv-p3mdw1-canary-7b7d5ffdc9-w9dc4-19-5F908E67-4C 2020-10-21 19:39:19.889770554 +0000 UTC m=+7309.445193874 Received: from herokuapp.com (unknown) by ismtpd0006p1iad2.sendgrid.net (SG) with ESMTP id pfM3c0JxQ82gX063ubBfkA for ; Wed, 21 Oct 2020 19:39:19.784 +0000 (UTC) Date: Wed, 21 Oct 2020 19:39:19 +0000 (UTC) From: daniel@dan42.com Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 76349 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 17278 X-Redmine-Issue-Author: Dan0042 X-Redmine-Sender: Dan0042 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?8sy4RigFvRTdBfCVJrT9zb2J88PC92TMQwdNgaWYaq6hMCYXABxudWTPSvPYl0?= =?us-ascii?Q?TkujR0hQEi9IW7fBrnNVHoe2ordLcKUW7UebkP=2F?= =?us-ascii?Q?wzL9ouFBJIroxk4fiA7i6XNtaMFkngm8+0M8PKz?= =?us-ascii?Q?ahTVq82ST52d=2Fmc+IBxacI4MsGpuT01EXyXyVY6?= =?us-ascii?Q?xpjCgOC1RYjutkm2OJh9qPvLM2KkRYtMGeO4xPU?= =?us-ascii?Q?DcV7gEpK1BGxwSLp8=3D?= To: ruby-core@ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== X-ML-Name: ruby-core X-Mail-Count: 100479 Subject: [ruby-core:100479] [Ruby master Feature#17278] On-demand sharing of constants 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 #17278 has been reported by Dan0042 (Daniel DeLorme). ---------------------------------------- Feature #17278: On-demand sharing of constants for Ractor https://bugs.ruby-lang.org/issues/17278 * Author: Dan0042 (Daniel DeLorme) * Status: Open * Priority: Normal ---------------------------------------- ### Description This proposal aims to reduce (but not eliminate) the need for freezing/sharing boilerplate code needed by ractors. ```ruby A = [1, [2, [3, 4]]] H = {a: "a"} Ractor.new do p A #A is not actually modified anywhere, so ok end.take H[:b] = "b" #H was never touched by ractor, so ok ``` ## Background Ractors require objects to be preemptively deep-frozen in order to be shared between ractors. This has an especially visible and restrictive effect on globals and constants. I tried thinking of a different way, and maybe I found one. So please allow me to humbly present this possibility. ## Proposal A constant would be by default in a "auto-shareable" state (A) which can change atomically to either (B) "non-shareable" if it is modified by the main ractor (C) "shareable" (and frozen) if it is accessed by a non-main ractor In detail: 1. When an object is assigned to a constant, it is added to a list of ractor-reachable objects 2. When the first ractor is created, the objects in that list are recursively marked with FL_AUTOSHARE * after this point, constant assignments result directly in FL_AUTOSHARE 3. In the main ractor, a call to `rb_check_frozen` (meaning the object is being modified) will 1. if FL_AUTOSHARE is set (state A) * [with ractor lock] * unless object is shareable * unset FL_AUTOSHARE (state B) 2. raise error if frozen * ideally with different message if object has FL_SHAREABLE 4. When a non-main ractor accesses a non-shareable constant 1. if object referenced by constant has FL_AUTOSHARE set (state A) * [with ractor lock] * if all objects recursively are still marked with FL_AUTOSHARE * make_shareable (state C) * else * unset top objects's FL_AUTOSHARE (state B) 2. raise error if not shareable ## Result So in the case that these 2 things happen in parallel: 1) main ractor modifies content of constant X 2) non-main ractor accesses constant X There are 2 possible outcomes: a) main ractor error "can't modify frozen/shared object" b) non-main ractor error "can not access non-shareable objects in constant X" ## Benefits In the normal case where non-frozen constants are left untouched after being assigned, this allows to skip a lot of `.freeze` or `Ractor.make_shareable` or `# shareable_constant_value: true` boilerplate. When you get the error "can not access non-sharable objects in constant X by non-main Ractor", first you have to make that constant X shareable. Then this can trigger a secondary error that X is frozen, that you also have to debug. This way cuts the debugging in half by skipping directly to the FrozenError. ## Downsides When you get the error "can not access non-sharable objects in constant X by non-main Ractor" you may want to solve the issue by e.g. copying the constant X rather than freezing it. This way makes it slightly harder to find where X is being accessed in the non-main ractor. In the case of conflict, whether the error occurs in the main ractor or the non-main ractor can be non-deterministic. ## Applicability This probably applies as well to global variables, class variables, and class instance variables. -- https://bugs.ruby-lang.org/