You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/language/ractor.md
+61-73Lines changed: 61 additions & 73 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,28 +4,27 @@ Ractors are designed to provide parallel execution of Ruby code without thread-s
4
4
5
5
## Summary
6
6
7
-
### Multiple Ractors in an interpreter process
7
+
### Multiple Ractors in a ruby process
8
8
9
-
You can create multiple Ractors which can run ruby code in parallel.
9
+
You can create multiple Ractors which can run ruby code in parallel with each other.
10
10
11
11
*`Ractor.new{ expr }` creates a new Ractor and `expr` can run in parallel with other ractors on a multi-core computer.
12
-
* Ruby processes start with one Ractor (called the *main Ractor*).
13
-
* If the main Ractor terminates, all other Ractors receive termination requests, similar to how threads behave.
14
-
* Each Ractor contains one or more Threads.
15
-
* Threads within the same Ractor share a Ractor-wide global lock (GVL in MRI terminology), so they can't run in parallel wich each other (without releasing the GVL explicitly in C extensions). Threads in different ractors can run in parallel.
16
-
* The overhead of creating a Ractor is slightly above the overhead of creating a Thread.
12
+
* Ruby processes start with one ractor (called the *main ractor*).
13
+
* If the main ractor terminates, all other ractors receive termination requests, similar to how threads behave.
14
+
* Each Ractor contains one or more `Thread`s.
15
+
* Threads within the same ractor share a ractor-wide global lock (GVL in MRI terminology), so they can't run in parallel wich each other (without releasing the GVL explicitly in C extensions). Threads in different ractors can run in parallel.
16
+
* The overhead of creating a ractor is slightly above the overhead of creating a thread.
17
17
18
18
### Limited sharing between Ractors
19
19
20
-
Ractors don't share all objects, unlike Threads which can access any object other than objects stored in another Thread's thread-locals.
20
+
Ractors don't share all objects, unlike threads which can access any object other than objects stored in another thread's thread-locals.
21
21
22
-
* Most objects are *Unshareable objects*. Unshareable objects can only be used by the ractor that instantiated them, so you don't need to worry about thread-safety issues resulting from using the object concurrently across Ractors.
23
-
* Some objects are *Shareable objects*. Here is an incomplete list to give you an idea:
24
-
* Immutable objects: these are frozen objects which don't refer to unshareable-objects.
25
-
*`i = 123`: `i` is an immutable object.
26
-
*`s = "str".freeze`: `s` is an immutable object.
27
-
*`a = [1, [2], 3].freeze`: `a` is not an immutable object because `a` refers to the unshareable-object `[2]` (which is not frozen).
28
-
*`h = {c: Object}.freeze`: `h` is an immutable object because `h` refers to the Symbol `:c` and the shareable `Object` class.
22
+
* Most objects are *unshareable objects*. Unshareable objects can only be used by the ractor that instantiated them, so you don't need to worry about thread-safety issues resulting from using the object concurrently across ractors.
23
+
* Some objects are *shareable objects*. Here is an incomplete list to give you an idea:
24
+
*`i = 123`: All `Integer`s are shareable.
25
+
*`s = "str".freeze`: Frozen strings are shareable if they have no instance variables that refer to unshareable objects.
26
+
*`a = [1, [2], 3].freeze`: `a` is not a shareable object because `a` refers to the unshareable object `[2]` (this Array is not frozen).
27
+
*`h = {c: Object}.freeze`: `h` is shareable because `Symbol`s and `Class`es are shareable, and the Hash is frozen.
29
28
* Class/Module objects are always shareable, even if they refer to unshareable objects.
30
29
* Special shareable objects
31
30
* Ractor objects themselves are shareable.
@@ -53,17 +52,17 @@ All Ractors have a default port, which `Ractor#send`, `Ractor.receive` (etc) wil
53
52
54
53
To send unshareable objects to another ractor, objects are either copied or moved.
55
54
56
-
* Copy: deep-copies the object to the other ractor.
55
+
* Copy: deep-copies the object to the other ractor. All unshareable objects will be `Kernel#clone`ed.
57
56
* Move: moves membership to another ractor.
58
-
* The sending Ractor can not access the moved object after it moves.
59
-
* There is a guarantee that only one Ractor can access an unshareable object at once.
57
+
* The sending ractor can not access the moved object after it moves.
58
+
* There is a guarantee that only one ractor can access an unshareable object at once.
60
59
61
60
### Thread-safety
62
61
63
62
Ractors help to write thread-safe, concurrent programs. They allow sharing of data only through explicit message passing for
64
63
unshareable objects. Shareable objects are guaranteed to work correctly across ractors, even if the ractors are running in parallel.
65
-
This guarantee, however, only applies across ractors. You still need to use Mutexes and other thread-safety tools within a ractor if
66
-
you're using multiple ruby Threads.
64
+
This guarantee, however, only applies across ractors. You still need to use `Mutex`es and other thread-safety tools within a ractor if
65
+
you're using multiple ruby `Thread`s.
67
66
68
67
* Most objects are unshareable. You can't create data-races across ractors due to the inability to use these objects across ractors.
69
68
* Shareable objects are protected by locks (or otherwise don't need to be) so they can be used by more than one ractor at once.
@@ -72,7 +71,7 @@ you're using multiple ruby Threads.
72
71
73
72
### `Ractor.new`
74
73
75
-
*`Ractor.new{ expr }` creates a Ractor.
74
+
*`Ractor.new{ expr }` creates a Ractor.
76
75
77
76
```ruby
78
77
# Ractor.new with a block creates a new Ractor
@@ -84,7 +83,6 @@ end
84
83
r =Ractor.newname:'my-first-ractor'do
85
84
end
86
85
87
-
# and Ractor#name returns its name.
88
86
r.name #=> 'my-first-ractor'
89
87
```
90
88
@@ -164,40 +162,41 @@ end
164
162
165
163
## Communication between Ractors
166
164
167
-
Communication between Ractors is achieved by sending and receiving messages. There are two ways to communicate:
165
+
Communication between ractors is achieved by sending and receiving messages. There are two ways to communicate:
168
166
169
167
* (1) Sending and receiving messages via `Ractor::Port`
* (2) Using shareable container objects. For example, the Ractor::TVar gem ([ko1/ractor-tvar](https://github.com/ko1/ractor-tvar))
172
169
173
170
Users can control program execution timing with (1), but should not control with (2) (only perform critical sections).
174
171
175
172
For sending and receiving messages, these are the fundamental APIs:
176
173
177
174
* send/receive via `Ractor::Port`.
178
-
*`Ractor::Port#send(obj)` (`Ractor::Port#<<(obj)` is an alias) sends a message to the port. Ports are connected to an infinite size incoming queue so it will never block the caller.
179
-
*`Ractor::Port#receive` dequeues a message from its own incoming queue. If the incoming queue is empty, `Ractor::Port#receive` will block the execution of the current Thread.
175
+
*`Ractor::Port#send(obj)` (`Ractor::Port#<<(obj)` is an alias) sends a message to the port. Ports are connected to an infinite size incoming queue so sending will never block the caller.
176
+
*`Ractor::Port#receive` dequeues a message from its own incoming queue. If the incoming queue is empty, `Ractor::Port#receive` will block the execution of the current Thread until a message is sent.
180
177
*`Ractor#send` and `Ractor.receive` use ports (their default port) internally, so are conceptually similar to the above.
181
178
* You can close a `Ractor::Port` by `Ractor::Port#close`. A port can only be closed by the ractor that created it.
182
179
* If a port is closed, you can't `send` to it. Doing so raises an exception.
183
-
* When a Ractor is terminated, the Ractor's ports are automatically closed.
180
+
* When a ractor is terminated, the ractor's ports are automatically closed.
184
181
* You can wait for a ractor's termination and receive its return value with `Ractor#value`. This is similar to `Thread#value`.
185
182
186
183
There are 3 ways to send an object as a message:
187
184
188
185
1) Send a reference: sending a shareable object sends only a reference to the object (fast).
186
+
189
187
2) Copy an object: sending an unshareable object through copying it deeply (can be slow). Note that you can not send an object this way which does not support deep copy. Some `T_DATA` objects (objects whose class is defined in a C extension, such as `StringIO`) are not supported.
188
+
190
189
3) Move an object: sending an unshareable object across ractors with a membership change. The sending Ractor can not access the moved object after moving it, otherwise an exception will be raised. Implementation note: `T_DATA` objects are not supported.
191
190
192
191
You can choose between "Copy" and "Move" by the `move:` keyword, `Ractor#send(obj, move: true/false)`. The default is `false` ("Copy"). However, if the object is shareable it will automatically use `move`.
193
192
194
193
### Wait for multiple Ractors with `Ractor.select`
195
194
196
195
You can wait for messages on multiple ports at once.
197
-
The return value of `Ractor.select()` is `[port, msg]` where `port` is a ready port and `msg` is received message.
196
+
The return value of `Ractor.select()` is `[port, msg]` where `port` is a ready port and `msg` is the received message.
198
197
199
-
To make it convenient, `Ractor.select` can also accept Ractors. In this case, it waits for their termination.
200
-
The return value of `Ractor.select()` is `[r, msg]` where `r` is a terminated Ractor and `msg` is the value of Ractor's block.
198
+
To make it convenient, `Ractor.select` can also accept ractors. In this case, it waits for their termination.
199
+
The return value of `Ractor.select()` is `[r, msg]` where `r` is a terminated Ractor and `msg` is the value of the ractor's block.
201
200
202
201
Wait for a single ractor (same as `Ractor#value`):
203
202
@@ -208,36 +207,33 @@ r, obj = Ractor.select(r1)
208
207
r == r1 and obj =='r1'#=> true
209
208
```
210
209
211
-
Waiting for two ractors:
210
+
Wait for two ractors:
212
211
213
212
```ruby
214
213
r1 =Ractor.new{'r1'}
215
214
r2 =Ractor.new{'r2'}
216
215
rs = [r1, r2]
217
216
values = []
218
217
219
-
# Wait for r1 or r2's termination
220
-
r, obj =Ractor.select(*rs)
221
-
rs.delete(r)
222
-
values << obj
218
+
while rs.any?
219
+
r, obj =Ractor.select(*rs)
220
+
rs.delete(r)
221
+
values << obj
222
+
end
223
223
224
-
# Second try (rs only contain not-closed ractors)
225
-
r, obj =Ractor.select(*rs)
226
-
rs.delete(r)
227
-
values << obj
228
224
values.sort == ['r1', 'r2'] #=> true
229
225
```
230
226
231
227
NOTE: Using `Ractor.select()` on a very large number of ractors has the same issue as `select(2)` currently.
232
228
233
-
### Closing Ractor's ports
229
+
### Closing ports
234
230
235
-
*`Ractor::Port#close`close the ports (similar to `Queue#close`).
236
-
*`port.send(obj)`where `port` is closed, will raise an exception.
231
+
*`Ractor::Port#close`closes the port (similar to `Queue#close`).
232
+
*`port.send(obj)` will raise an exception when the port is closed.
237
233
* When the queue connected to the port is empty and port is closed, `Ractor::Port#receive` raises an exception. If the queue is not empty, it dequeues an object without exceptions.
238
234
* When a Ractor terminates, the ports are closed automatically.
*`Symbol`, frozen `String` objects that don't refer to unshareables, `true`, `false`, `nil`
343
336
*`Regexp` objects, if they have no instance variables or their instance variables refer only to shareables
344
-
* Class and Module objects
345
-
*`Ractor` and other special objects which care about synchronization
337
+
*`Class` and `Module` objects
338
+
*`Ractor` and other special objects which deal with synchronization
346
339
347
340
To make objects shareable, `Ractor.make_shareable(obj)` is provided. It tries to make the object shareable by freezing `obj` and recursively traversing its references to freeze them all. This method accepts the `copy:` keyword (default value is false). `Ractor.make_shareable(obj, copy: true)` tries to make a deep copy of `obj` and make the copied object shareable. `Ractor.make_shareable(copy: false)` has no effect on an already shareable object. If the object cannot be made shareable, a `Ractor::Error` exception will be raised.
348
341
349
-
## Language changes to isolate unshareable objects between Ractors
342
+
## Language changes to limit sharing between Ractors
350
343
351
-
To isolate unshareable objects between Ractors, we introduced additional language semantics on multi-Ractor Ruby programs.
344
+
To isolate unshareable objects across ractors, we introduced additional language semantics for multi-ractor Ruby programs.
352
345
353
-
Note that without using Ractors, these additional semantics are not needed (100% compatible with Ruby 2).
346
+
Note that when not using ractors, these additional semantics are not needed (100% compatible with Ruby 2).
354
347
355
348
### Global variables
356
349
@@ -369,11 +362,11 @@ rescue Ractor::RemoteError => e
369
362
end
370
363
```
371
364
372
-
Note that some special global variables, such as `$stdin`, `$stdout` and `$stderr` are Ractor-local. See [[Bug #17268]](https://bugs.ruby-lang.org/issues/17268) for more details.
365
+
Note that some special global variables, such as `$stdin`, `$stdout` and `$stderr` are local to each ractor. See [[Bug #17268]](https://bugs.ruby-lang.org/issues/17268) for more details.
373
366
374
367
### Instance variables of shareable objects
375
368
376
-
Instance variables of classes/modules can be accessed from non-main Ractors only if their values are shareable objects.
369
+
Instance variables of classes/modules can be accessed from non-main ractors only if their values are shareable objects.
377
370
378
371
```ruby
379
372
classC
@@ -514,15 +507,13 @@ The `shareable_constant_value` directive accepts the following modes (descriptio
514
507
* experimental_everything: replaced to `CONST = Ractor.make_shareable(expr)`.
515
508
* experimental_copy: replaced to `CONST = Ractor.make_shareable(expr, copy: true)`.
516
509
517
-
Except for the `none` mode (default), it is guaranteed that the constants in the file refer only to shareable objects.
510
+
Except for the `none` mode (default), it is guaranteed that these constants refer only to shareable objects.
518
511
519
-
See [doc/syntax/comments.rdoc](syntax/comments.rdoc) for more details.
512
+
See [syntax/comments.rdoc](../syntax/comments.rdoc) for more details.
520
513
521
514
### Shareable procs
522
515
523
-
Procs and lambdas are unshareable objects, even when they are frozen. To create an unshareable Proc, you must use `Ractor.shareable_proc { expr }`. Much like during Ractor creation, the Proc's block is isolated
524
-
from its outer environment, so it cannot access locals from the outside scope. `self` is also changed within the Proc to be `nil` by default, although a `self:` keyword can be provided if you want to customize
525
-
the value to a different shareable object.
516
+
Procs and lambdas are unshareable objects, even when they are frozen. To create an unshareable Proc, you must use `Ractor.shareable_proc { expr }`. Much like during Ractor creation, the proc's block is isolated from its outer environment, so it cannot access variables from the outside scope. `self` is also changed within the Proc to be `nil` by default, although a `self:` keyword can be provided if you want to customize the value to a different shareable object.
In order to dynamically define a method with `define_method` that can be used from different ractors, you must
542
-
define it with a shareable proc. Alternatively, you can use `class_eval` or `module_eval` with a String. Even though
543
-
the shareable proc's `self` is initially bound to `nil`, `define_method` will bind `self` to the correct value in the
544
-
method.
532
+
In order to dynamically define a method with `Module#define_method` that can be used from different ractors, you must define it with a shareable proc. Alternatively, you can use `Module#class_eval` or `Module#module_eval` with a String. Even though the shareable proc's `self` is initially bound to `nil`, `define_method` will bind `self` to the correct value in the method.
545
533
546
534
```ruby
547
535
classA
@@ -555,7 +543,7 @@ Ractor.new do
555
543
end.join
556
544
```
557
545
558
-
This isolation must be done to prevent the method from accessing captured outer variables across Ractors.
546
+
This isolation must be done to prevent the method from accessing and assigning captured outer variables across ractors.
0 commit comments