[#100284] [Ruby master Bug#17211] Test failures in ruby2.7.2 and ruby3.0~preview1 — utkarsh@...

Issue #17211 has been reported by utkarsh (Utkarsh Gupta).

10 messages 2020/10/02

[#100301] [Ruby master Feature#17215] Backport for arm64 optimizations that exist for power/x86 — jaruga@...

Issue #17215 has been reported by jaruga (Jun Aruga).

10 messages 2020/10/05

[#100329] [Ruby master Bug#17220] Rails Active Job integration test fails with Ruby 3.0.0 since 2038cc6cab6ceeffef3ec3a765c70ae684f829ed — yasuo.honda@...

Issue #17220 has been reported by yahonda (Yasuo Honda).

28 messages 2020/10/07

[#100332] [Ruby master Bug#17221] Relax the Fiber#transfer's limitation — ko1@...

Issue #17221 has been reported by ko1 (Koichi Sasada).

15 messages 2020/10/07

[#100348] [Ruby master Bug#17257] Integer#pow(0, 1) returns 1, which is incorrect — universato@...

Issue #17257 has been reported by universato (Yoshimine Sato).

13 messages 2020/10/09

[#100371] [Ruby master Feature#17260] Promote pattern matching to official feature — kazuki@...

Issue #17260 has been reported by ktsj (Kazuki Tsujimoto).

10 messages 2020/10/11

[#100383] [Ruby master Feature#17261] Software transactional memory (STM) for Threads and Ractors — ko1@...

Issue #17261 has been reported by ko1 (Koichi Sasada).

14 messages 2020/10/12

[#100401] [Ruby master Bug#17263] Fiber context switch degrades with number of fibers, limit on number of fibers — ciconia@...

Issue #17263 has been reported by ciconia (Sharon Rosner).

14 messages 2020/10/15

[#100422] [CommonRuby Feature#17265] Add `Bool` module — marcandre-ruby-core@...

Issue #17265 has been reported by marcandre (Marc-Andre Lafortune).

11 messages 2020/10/19

[#100466] [Ruby master Feature#17273] shareable_constant_value pragma — ko1@...

Issue #17273 has been reported by ko1 (Koichi Sasada).

14 messages 2020/10/21

[#100471] [Ruby master Feature#17277] Make Enumerator#with_index yield row and col indices for Matrix — grzegorz.jakubiak@...

Issue #17277 has been reported by greggzst (Grzegorz Jakubiak).

8 messages 2020/10/21

[#100479] [Ruby master Feature#17278] On-demand sharing of constants for Ractor [email protected]

Issue #17278 has been reported by Dan0042 (Daniel DeLorme).

13 messages 2020/10/21

[#100534] [Ruby master Feature#17284] Shareable Proc — ko1@...

Issue #17284 has been reported by ko1 (Koichi Sasada).

16 messages 2020/10/25

[#100597] [Ruby master Feature#17288] Optimize __send__ call with a literal method name — muraken@...

Issue #17288 has been reported by mrkn (Kenta Murata).

13 messages 2020/10/27

[#100669] [Ruby master Feature#17295] Feature: Create a directory and file with Pathname#touch — get.codetriage@...

Issue #17295 has been reported by schneems (Richard Schneeman).

9 messages 2020/10/30

[#100673] [Ruby master Feature#17298] Ractor's basket communication APIs — ko1@...

Issue #17298 has been reported by ko1 (Koichi Sasada).

15 messages 2020/10/30

[#100675] [Ruby master Misc#17299] DevelopersMeeting20201120Japan — mame@...

Issue #17299 has been reported by mame (Yusuke Endoh).

11 messages 2020/10/31

[ruby-core:100595] [Ruby master Feature#17284] Shareable Proc

From: ko1@...
Date: 2020-10-27 04:25:49 UTC
List: ruby-core #100595
Issue #17284 has been updated by ko1 (Koichi Sasada).


marcandre (Marc-Andre Lafortune) wrote in #note-13:
> ko1 (Koichi Sasada) wrote in #note-12:
> > My understanding:
> > 
> > `Proc#detach` do 
> > 
> > * 1. allocate a snapshot area
> > * 2. copy object (which can be referred from Proc's variables) references to snapshot
> > 
> > `Proc#call` do
> > 
> > * 1. allocate outer-lvars area for outer variables
> > * 2. copy snapshot refs to outer-lvars area
> > 
> > `Proc#freeze` in this ticket is similar to `Proc#detach`, but does not do special at `Proc#call`.
> 
> Indeed. But `freeze` must do special check for reassignments.

Is "reassignments" `a = 1`?
Yes. It is checked at `#freeze` timing (if there is assignments to outer variables, raise an error).

> What I dislike about `Proc#freeze` is that it does not make intuitive sense to me. A `Proc` is not mutable per say. Calling `Proc.new { ... }` does not change the Proc.
> Also, if after `Proc#freeze` you can reassign `a` outside the block and has no effect inside the block, then they are different local variables. It is not intuitive for me to disallow reassigning one and not the other.

There is two positions, environments (lvar space) is belong to a Proc, or a Proc only refers to environments.
`Proc#freeze` terminology uses the position "environments (lvar space) is belong to a Proc".

And in fact, environments are different object in implementation. So your intuition is also correct.

I use `Proc#freeze` terminology to explain the design to discuss this ticket, and I don't care to change (or remove) this name.


> I will agree that in general, these variables will not be reassigned anyways so it won't matter much.

Do you have any useful example of outer-variable reassignment on `Proc#detach` semantics?

> I just think it is easier to understand if you are allowed to reassigning it.

You say it is easy to explain the feature, right? It can be.
But I also think if I can set outer-variables, I expect they are shared with other Procs (bindings).

So freezing semantics is easy/no-misunderstanding feature I think.

> Do you think there would be a noticeable difference in performance either way?

mmm. copying overhead and memory overhead? not so big difference I think.
Actually, current Proc#freeze implementation is slow and memory consuming because it copies all readable variables.

----------------------------------------
Feature #17284: Shareable Proc
https://bugs.ruby-lang.org/issues/17284#change-88226

* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
----------------------------------------
For some reasons, we need to provide a way to make sharable Proc between ractors.

* (1) A block for the `Ractor.new`.
* (2) Send a proc between ractors.
* (3) A block for global callback methods: `define_method` ([Bug #17159]), `TracePoint`, ...

For (1), we use `Proc#isolate` (`isolate` is temporary name here) which prohibit to access outer variables.

```ruby
a = 1
Proc.new{
  p a 
}.isolate # => can not isolate a Proc because it accesses outer variables (a).
          # error on `isolate` method call
```

There are no states to share, so it is okay.

For (2), `Proc#isolate` is one option because we can send parameters with an argument `call`.
But it should be a bit long.

```ruby
i, j, k = nil

pr = Proc.new do |i, j, k|
  p i, j, k
end.isolate

r = Ractor.new do |task, param|
  task.call(*param)
end

r.send([pr, [i, j, k]])

```

For (3), maybe we need to make more flexible Proc which can *read* outer block parameter on that snapshot (discussed in #17159).

Now, I named it with `freeze`, because it seems frozen Proc.

```ruby
a = 1

# try to read, and returns old value (snapshot at `freeze`)
pr = Proc.new{
  p a #=> 1
}
pr = pr.freeze
pr.call

a = 2

pr.call #=> 1


# try to write, and it is not allowed
pr2 = Proc.new{
  a = 1
}
pr2 = pr.freeze
#=> can not freeze a Proc because it accesses outer variables (a). (ArgumentError)
```

To share the "frozen" Proc between ractors, outer values should be (deep) frozen. It means readable values (in above case, `a`) should be shareable.
Now we named it `Proc#shareable!`

```ruby
a = [1, [2, 3]]
pr = Proc.new{
  p a.frozen? #=> true
}.shareable!

a[0] = 0 #=> frozen error
```

This ticket has three different variant of mutability and shareability for Proc.

|               | outer lvar    | shareable  | freeze/making shareable other objects
|---------------|---------------|------------|------------------------------------------
|a. isolate     | N/A           | Yes        | No
|b. freeze      | allow to read | No         | No
|c. shareable!  | allow to read | Yes        | Yes

I want to introduce functionality of `shareable!`, but not sure the Ruby-level API.

I think (b) `freeze` for this semantics is good name because it only allows to read-only local variables.
However, it is not enough to make a sharable Proc because read objects from the Proc should be also sharable.

Making `freeze` with (c) `shareable!` functionality is one idea, but I think `freeze` should not deep-freezing because it is very surprising that read objects become the sharable (== frozen) for usual Ruby users.
Maybe `Ractor.make_sharable(pr)` makes `pr` sharable is no surprise because it is good declaration the `pr` should be shareable, even if the read objects from `pr` become shareable (== frozen).

Removing (a) `isolate` and using (c) `shareable!` at `Ractor.new(&b)` is one idea, but I think it is surprising that they can access outer local variables, but the they can not access newly assigned variables as usual blocks.

```
a = 1
Ractor.new do
  p a # only 1
end

a = 2
```

(a) `isolate` does not have such issue because all outer lvars accesses are not allowed == easy to understand, easy to debug.

In practice, accessing outer variables with multi-ractor program is very useful because we need to declare same local variables if we want to access them from different ractors.

The following example is from [Feature #17261]:

```ruby
tv1 = Thread::TVar.new(0)
tv2 = Thread::TVar.new(0)

r1 = Ractor.new tv1, tv2 do |tv1, tv2|    # <-- here
  loop do
    Thread.atomically do
      v1, v2 = tv1.value, tv2.value
      raise if v1 != v2
    end
  end
end
```

With (c) `shareable!` semantics, it is easier to write:

```ruby
tv1 = Thread::TVar.new(0)
tv2 = Thread::TVar.new(0)

r1 = Ractor.new do
  loop do
    Thread.atomically do
      v1, v2 = tv1.value, tv2.value
      raise if v1 != v2
    end
  end
end
```

Above example is also enable to make more simple:

```ruby
i, j, k = nil

pr = Proc.new do
  p i, j, k
end

r = Ractor.new do |task|
  task.call
end

r.send(pr)

```

However, using this semantics (`shareable!`) can freeze extra-variables in accidents:


```ruby
a = [1, 2, 3]

Ractor.new do
  do_something if a.length > 0
end

a << 4 # raises FrozenError
```

It is clear that there is a syntax or method to apply `shareable!` functionality.

```ruby
a = [1, 2, 3]
Ractor.new &(Ractor.make_shareable(Proc.new{ a.length ... })
```

It can be used with `define_method` which can invoke from ractors:

```ruby
define_method(name, Ractor.make_shareable(Proc.new{ ... }))`
```

But it is too long.

There are implementations for (a), (b) and (c), but the API is not fixed, so there is no PR now.

I'm thinking to introduce (c)'s feature in `Ractor.make_sharaeble(pr)`.
To use with `define_method`, maybe it should be more friendly. Ideally, new syntax is great.

There is no conclusion, and your comments are welcome.

Thanks,
Koichi




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread