[#100309] How to use backport custom field — Jun Aruga <jaruga@...>
Please allow my ignorance.
9 messages
2020/10/06
[#100310] Re: How to use backport custom field
— "NARUSE, Yui" <naruse@...>
2020/10/06
IkJhY2twb3J0IGN1c3RvbSBmaWVsZCIgaXMgb25seSBhdmFpbGFibGUgZm9yIHRpY2tldHMgd2hv
[#100311] Re: How to use backport custom field
— Jun Aruga <jaruga@...>
2020/10/06
On Tue, Oct 6, 2020 at 4:44 PM NARUSE, Yui <[email protected]> wrote:
[#100314] Re: How to use backport custom field
— "NARUSE, Yui" <naruse@...>
2020/10/06
VGhhbmsgeW91IGZvciBjb25maXJtYXRpb24uCkkgY2hlY2tlZCBhZ2FpbiBhbmQgdG8gZWRpdCBi
[#100322] Re: How to use backport custom field
— Jun Aruga <jaruga@...>
2020/10/07
On Tue, Oct 6, 2020 at 7:25 PM NARUSE, Yui <[email protected]> wrote:
[#100326] Re: How to use backport custom field
— "NARUSE, Yui" <naruse@...>
2020/10/07
SSBhZGRlZCB5b3UgdG8gIlJlcG9ydGVyIiByb2xlIGluIHRoZSBwcm9qZWN0CgoyMDIw5bm0MTDm
[#100327] Re: How to use backport custom field
— Jun Aruga <jaruga@...>
2020/10/07
On Wed, Oct 7, 2020 at 1:42 PM NARUSE, Yui <[email protected]> wrote:
[ruby-core:100571] [Ruby master Feature#17100] Ractor: a proposal for a new concurrent abstraction without thread-safety issues
From:
nobu@...
Date:
2020-10-26 13:41:03 UTC
List:
ruby-core #100571
Issue #17100 has been updated by nobu (Nobuyoshi Nakada).
duerst (Martin D=FCrst) wrote in #note-41:
> Send would be the best name, except that that we are not in a vacuum. It =
is bad practice to have one and the same name for two fundamentally differe=
nt and colliding functionalities.
Agreed.
`Kernel#send` should be deprecated because of `BasicObject#__send__`.
----------------------------------------
Feature #17100: Ractor: a proposal for a new concurrent abstraction without=
thread-safety issues
https://bugs.ruby-lang.org/issues/17100#change-88203
* Author: ko1 (Koichi Sasada)
* Status: Closed
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
----------------------------------------
# Ractor: a proposal for a new concurrent abstraction without thread-safety=
issues
## Abstract
This ticket proposes a new concurrent abstraction named "Ractor," Ruby's Ac=
tor-like feature (not an exact Actor-model).
Ractor achieves the following goals:
* Parallel execution in a Ruby interpreter process
* Avoidance of thread-safety issues (especially race issues) by limiting ob=
ject sharing
* Communication via copying and moving
I have been working on this proposal for a few years. The project name has =
been "Guild," but I renamed it to Ractor following Matz' preference.
Resources:
* Proposed specification: https://github.com/ko1/ruby/blob/ractor_parallel/=
doc/ractor.md
* My talk:
* (latest, but written in Japanese) http://atdot.net/~ko1/activities/2020=
_ruby3summit.pdf
* (old, API is changed) http://atdot.net/~ko1/activities/2018_rubykaigi20=
18.pdf
* (old, API is changed) http://atdot.net/~ko1/activities/2018_rubyconf201=
8.pdf
Current implementation is not complete (contains many bugs) but it passes t=
he current CI. I propose to merge it soon and try the API, and to continue =
working on the implementation on master branch.
## Background
MRI doesn't provide an in-process parallel computation feature because para=
llel "Threads" have many issues:
* Ruby programmers need to consider about Thread-safety more.
* Interpreter developers need to consider about Thread-safety more.
* Interpreter will slow down in single thread execution because of fine-gra=
in synchronization without clever optimizations.
The reason for these issues is "shared-everything" thread model.
## Proposal
To overcome the issues on multiple-threads, Ractor abstraction is proposed.=
This proposal consists of two layers: memory model and communication model.
Basics:
* Introduce "Ractor" as a new concurrent entity.
* Ractors run in parallel.
Memory model:
* Separate "shareable" objects and "un-shareable" objects among ractors run=
ning in parallel.
* Shareable objects:
* Immutable objects (frozen objects only refer to shareable objects)
* Class/module objects
* Special shareable objects (Ractor objects, and so on)
* Un-shareable objects: =
* Other objects
* Most objects are "un-shareable," which means we Ruby programmers and inte=
rpreter developers don't need to care about thread-safety in most cases.
* We only concentrate on synchronizing "shareable" objects.
* Compared with completely separated memory model (like MVM proposal), prog=
ramming will be easier.
* This model is similar to Racket's `Place` abstraction.
Communication model:
* Actor-like (not the same) message passing using `Ractor#send(obj)` and `R=
actor.recv`
* Pull-type communication using `Ractor.yield(obj)` and `Ractor#take`
* Support for multiple waiting using `Ractor.select(...)`
Actor-like model is the origin of the name of our proposal "Ractor" (Ruby's=
actor). However, currently, it is not an Actor model because we can't sele=
ct the message (using pattern match as in Erlang, Elixir, ...). This means =
that we can't have multiple communication channels. Instead of adopting an =
incomplete actor model, this proposal provides `yield`/`take` pair to handl=
e multiple channels. We discuss this topic later.
I strongly believe the memory model is promising. However, I'm not sure if =
the communication model is the best. This is why I introduced "experimental=
" warning.
Proposed specification: https://github.com/ko1/ruby/blob/ractor_parallel/do=
c/ractor.md
## Implementation
https://github.com/ruby/ruby/pull/3365
All GH actions pass.
I describe the implementation briefly.
### `rb_ractor_t`
Without Ractor, the VM-Thread-Fiber hierarchy is like this:
* The VM `rb_vm_t` manages running threads (`rb_thread_t`).
* A thread (`rb_thread_t`) points to a running fiber (`rb_fiber_t`).
With Ractor, we introduce a new layer `rb_ractor_t`:
* The VM `rb_vm_t` manages running ractors (`rb_ractor_t`).
* A Ractor manages running threads (`rb_thread_t`).
* A thread (`rb_thread_t`) points to a running fiber (`rb_fiber_t`).
`rb_ractor_t` has a GVL to manage threads (only one among a Ractor's thread=
s can run).
Ractor implementation is located in `ractor.h`, `ractor.c` and `ractor.rb`.
### VM-wide lock
VM-wide lock is introduced to protect VM global resources such as object sp=
ace. It should allow recursive lock, so the implementation is a monitor. We=
shall call it VM-wide monitor. For now, `RB_VM_LOCK_ENTER()` and `RB_VM_LO=
CK_LEAVE()` are provided to acquire/release the lock.
Note that it is different from the (current) GVL. A GVL is acquired anytime=
you run a Ruby thread. VM-wide lock is acquired only when accessing VM-wid=
e resources.
On single ractor mode (all Ruby scripts except my tests) =
### Object management and GC
* (1) All ractors share the object space.
* (2) Each GC event will stop all ractors, and a ractor GC works under barr=
ier synchronization.
* Barrier at `gc_enter()`
* marking, (lazy) sweeping, ...
* (3) Because all of the object space are shared by ractors, object creatio=
n is protected by VM-wide lock.
(2) and (3) have huge impact on performance. The plan is:
* For (2), introduce (semi-)separated object space. It would require a long=
time and Ruby 3.0 can't employ this technique.
* For (3), introduce free slot cache for every ractor; then most creations =
can be done without synchronization. It will be employed soon.
### Experimental warning
Currently, Ractor implementation and specification are not stable. So upon =
its first usage, `Ractor.new` will show a warning:
`warning: Ractor is experimental, and the behavior may change in future ver=
sions of Ruby! Also there are many implementation issues.`
## Discussion
### Actor-based and channel-based
I think there are two message passing approaches: Actor-based (as in Erlang=
, ...) and channel-based (as in Go, ...).
With channel-based approach, it is easy to manipulate multiple channels bec=
ause it manages them explicitly. Actor-based approach manipulates multiple =
channels with message pattern. The receiver can ignore unexpected structure=
d messages or put them on hold and can handle them after the behavior has c=
hanged (role of actor has changed).
Ractor has `send/recv` like Actor-model, but there is no pattern matching f=
eature. This is because we can't introduce new syntax, and I can't design a=
good API.
With channel-based approach, it is easy to design the API (for example, do =
`ch =3D Ractor::Channel.new` and share the `ch` that ractors can provide). =
However, I can't design a good API to handle exceptions among Ractors.
Regarding error handling, we propose a hybrid model using `send/recv`, `yie=
ld/take` pairs. `Ractor#take` can receive the source ractor's exception (li=
ke `Thread#join`). On Actor approach, we can detect when the destination Ra=
ctor is not working (killed) upon `Ractor#send(obj)`. A receiver ractor (wa=
iting for `Ractor.recv`) cannot detect the sender's trouble, but maybe the =
priority is not high. `Ractor#take` also detects the sender's (`Ractor.yiel=
d(obj)`) error, so the error can be propagated.
To handle multiple communication channels on Ractor, instead of using multi=
ple channels, we use *pipe* ractors.
```
# worker-pool (receive by send)
main # pipe.send(obj)
-> pipe # Ractor.yield Ractor.recv
->
worker1 # Ractor.yield(some_task pipe.take))
worker2 # Ractor.yield(some_task pipe.take))
worker3 # Ractor.yield(some_task pipe.take))
-> main # Ractor.select(worker1, worker2, worker3)
# if worker* causes an error, main can detect the error.
```
*pipe* ractors may look like channels. However, we don't need to introduce =
new classes with this technique (the implementation can omit Ractor creatio=
n for pipe ractors).
Maybe there are other possibilities. For example, if we can propagate the e=
rrors with channels, we can also consider a channel-model (we need to chang=
e the Ractor name :p then).
### Name of Ractor (and Guild)
When I proposed Guild in 2016, I regarded "move" message-passing (see speci=
fication) to be characteristic of it, and I called this feature "moving mem=
bership." This is why the name "Guild" was chosen. However Matz pointed out=
that this move semantics is not used frequently, and he asked me to change=
the name. Also, someone had already been using the class name "Guild."
"Ractor" is short and is not an existing class; this is why I choose "Racto=
r."
I understand people may confuse it with "Reactor."
## TODO
There are many remaining tasks.
### Protection
Many VM-wide (process-wide) resources are not protected correctly, so using=
Ractor on a complicated program can cause critical bugs (`[BUG]`). Most gl=
obal resource are managed by global variables, so we need to check them cor=
rectly.
### C-methods
Currently, C-methods (methods written in C and defined with `rb_define_meth=
od()`) run in parallel. It means that thread-unsafe code can run in paralle=
l. To solve this issue, I plan the following:
(1) Introduce thread-unsafe label for methods
It is impossible to make all C-methods thread-safe, especially for C-method=
s in third party C-extensions. To protect them, label these (possibly) thre=
ad-unsafe C-methods as "thread-unsafe."
When "unsafe"-labeled C methods are invoked, they acquire a VM-wide lock. T=
his VM-wide lock should check for recursiveness (so this lock should be a m=
onitor) and escaping (exceptions). Currently, VM-wide lock doesn't check fo=
r escaping, but that should be implemented soon.
(2) Built-in C-methods
I'll fix most of the builtin C-methods (String, Array, ...) so that they wi=
ll become thread-safe. If it is not easy, I'll use thread-unsafe label.
### Copying and moving
Currently, Marshal protocol makes deep copy on message communication. Howev=
er, Marshal protocol doesn't support some objects like `Ractor` objects, so=
we need to modify them.
Only a few types are supported for moving, so we need to write more.
### "GVL" naming
Currently, the source code contains the name "GVL" for Ractor local locks. =
Maybe they should be renamed.
### Performance
To introduce fine-grained lock, performance tuning is needed.
### Bug fixes
many many ....
## Conclusion
This ticket proposes a new concurrent abstraction "Ractor." I think Ruby 3 =
can ship with Ractor under "experimental" status.
-- =
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:[email protected]?subject=3Dunsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>