ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: eregontp@gmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:100531] [Ruby master Feature#17278] On-demand sharing of constants for Ractor
Date: Sun, 25 Oct 2020 13:21:57 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-88155.20201025132156.11019@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-17278.20201021193919.11019@ruby-lang.org

Issue #17278 has been updated by Eregon (Benoit Daloze).


A performance concern is this will make the first access to any constant need the process-global lock (or a more fine-grained lock, but then it will increase footprint).
That's not negligible, especially when considering to reuse JIT'ed code, where the JIT'ed code would have to handle that special first access and have an extra check and branch for it.

Also, and maybe more clearly, it would require every single Ruby constant read in the main Ractor to check that FL_AUTOSHARE flag, whether or not Ractors are used.

----------------------------------------
Feature #17278: On-demand sharing of constants for Ractor
https://bugs.ruby-lang.org/issues/17278#change-88155

* 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/

  parent reply	other threads:[~2020-10-25 13:22 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-21 19:39 [ruby-core:100479] [Ruby master Feature#17278] On-demand sharing of constants for Ractor daniel
2020-10-21 22:56 ` [ruby-core:100481] " ko1
2020-10-21 23:37 ` [ruby-core:100483] " daniel
2020-10-22  1:50 ` [ruby-core:100487] " ko1
2020-10-22  3:11 ` [ruby-core:100489] " daniel
2020-10-25 13:21 ` eregontp [this message]
2020-10-25 13:33 ` [ruby-core:100532] " eregontp
2020-10-26  3:15 ` [ruby-core:100544] " daniel
2020-10-26  9:04 ` [ruby-core:100566] " matz
2020-10-26 15:24 ` [ruby-core:100574] " daniel
2020-10-26 16:21 ` [ruby-core:100580] " ko1
2020-11-17 19:04 ` [ruby-core:100915] " daniel
2021-02-01 16:24 ` [ruby-core:102362] " daniel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-88155.20201025132156.11019@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).