rbs 3.10.2 → 4.0.0.dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/comments.yml +2 -2
- data/.github/workflows/ruby.yml +24 -34
- data/.github/workflows/typecheck.yml +3 -1
- data/.github/workflows/windows.yml +2 -2
- data/.gitignore +0 -4
- data/CHANGELOG.md +0 -107
- data/README.md +1 -38
- data/Rakefile +20 -142
- data/Steepfile +1 -0
- data/config.yml +43 -1
- data/core/array.rbs +46 -96
- data/core/comparable.rbs +6 -13
- data/core/complex.rbs +25 -40
- data/core/dir.rbs +4 -4
- data/core/encoding.rbs +9 -6
- data/core/enumerable.rbs +3 -90
- data/core/enumerator.rbs +1 -43
- data/core/errno.rbs +0 -8
- data/core/errors.rbs +1 -28
- data/core/exception.rbs +2 -2
- data/core/fiber.rbs +20 -29
- data/core/file.rbs +19 -49
- data/core/file_test.rbs +1 -1
- data/core/float.rbs +33 -224
- data/core/gc.rbs +281 -417
- data/core/hash.rbs +727 -1023
- data/core/integer.rbs +63 -104
- data/core/io/buffer.rbs +10 -21
- data/core/io/wait.rbs +33 -11
- data/core/io.rbs +12 -14
- data/core/kernel.rbs +51 -61
- data/core/marshal.rbs +1 -1
- data/core/match_data.rbs +1 -1
- data/core/math.rbs +3 -42
- data/core/method.rbs +25 -63
- data/core/module.rbs +23 -101
- data/core/nil_class.rbs +3 -3
- data/core/numeric.rbs +17 -25
- data/core/object.rbs +3 -3
- data/core/object_space.rbs +15 -21
- data/core/proc.rbs +24 -30
- data/core/process.rbs +2 -2
- data/core/ractor.rbs +509 -361
- data/core/range.rbs +8 -7
- data/core/rational.rbs +34 -56
- data/core/rbs/unnamed/argf.rbs +2 -2
- data/core/rbs/unnamed/env_class.rbs +1 -1
- data/core/rbs/unnamed/random.rbs +2 -4
- data/core/regexp.rbs +20 -25
- data/core/ruby_vm.rbs +4 -6
- data/core/rubygems/errors.rbs +70 -3
- data/core/rubygems/rubygems.rbs +79 -11
- data/core/rubygems/version.rbs +3 -2
- data/core/set.rbs +359 -488
- data/core/signal.rbs +14 -24
- data/core/string.rbs +1236 -3165
- data/core/struct.rbs +1 -1
- data/core/symbol.rbs +11 -17
- data/core/thread.rbs +33 -95
- data/core/time.rbs +9 -35
- data/core/trace_point.rbs +4 -7
- data/core/unbound_method.rbs +6 -14
- data/docs/collection.md +2 -2
- data/docs/gem.md +1 -0
- data/docs/sigs.md +3 -3
- data/ext/rbs_extension/ast_translation.c +1077 -944
- data/ext/rbs_extension/ast_translation.h +0 -7
- data/ext/rbs_extension/class_constants.c +83 -71
- data/ext/rbs_extension/class_constants.h +7 -4
- data/ext/rbs_extension/extconf.rb +2 -24
- data/ext/rbs_extension/legacy_location.c +172 -173
- data/ext/rbs_extension/legacy_location.h +3 -8
- data/ext/rbs_extension/main.c +289 -239
- data/ext/rbs_extension/rbs_extension.h +0 -3
- data/ext/rbs_extension/rbs_string_bridging.h +0 -4
- data/include/rbs/ast.h +98 -37
- data/include/rbs/defines.h +12 -38
- data/include/rbs/lexer.h +114 -126
- data/include/rbs/location.h +14 -14
- data/include/rbs/parser.h +37 -21
- data/include/rbs/string.h +5 -3
- data/include/rbs/util/rbs_allocator.h +19 -40
- data/include/rbs/util/rbs_assert.h +1 -12
- data/include/rbs/util/rbs_constant_pool.h +3 -3
- data/include/rbs/util/rbs_encoding.h +1 -3
- data/include/rbs/util/rbs_unescape.h +1 -2
- data/lib/rbs/ast/ruby/annotations.rb +119 -0
- data/lib/rbs/ast/ruby/comment_block.rb +221 -0
- data/lib/rbs/ast/ruby/declarations.rb +86 -0
- data/lib/rbs/ast/ruby/helpers/constant_helper.rb +24 -0
- data/lib/rbs/ast/ruby/helpers/location_helper.rb +15 -0
- data/lib/rbs/ast/ruby/members.rb +213 -0
- data/lib/rbs/buffer.rb +104 -24
- data/lib/rbs/cli/validate.rb +40 -35
- data/lib/rbs/cli.rb +5 -6
- data/lib/rbs/collection/config/lockfile_generator.rb +0 -8
- data/lib/rbs/collection.rb +0 -1
- data/lib/rbs/definition.rb +6 -1
- data/lib/rbs/definition_builder/ancestor_builder.rb +65 -62
- data/lib/rbs/definition_builder/method_builder.rb +45 -30
- data/lib/rbs/definition_builder.rb +44 -9
- data/lib/rbs/environment/class_entry.rb +69 -0
- data/lib/rbs/environment/module_entry.rb +66 -0
- data/lib/rbs/environment.rb +244 -218
- data/lib/rbs/environment_loader.rb +8 -2
- data/lib/rbs/errors.rb +5 -4
- data/lib/rbs/inline_parser/comment_association.rb +117 -0
- data/lib/rbs/inline_parser.rb +206 -0
- data/lib/rbs/location_aux.rb +35 -3
- data/lib/rbs/parser_aux.rb +11 -6
- data/lib/rbs/prototype/runtime.rb +2 -2
- data/lib/rbs/resolver/constant_resolver.rb +2 -2
- data/lib/rbs/resolver/type_name_resolver.rb +38 -124
- data/lib/rbs/source.rb +99 -0
- data/lib/rbs/subtractor.rb +5 -6
- data/lib/rbs/test/type_check.rb +0 -14
- data/lib/rbs/types.rb +1 -3
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs.rb +13 -1
- data/lib/rdoc/discover.rb +1 -1
- data/lib/rdoc_plugin/parser.rb +1 -1
- data/rbs.gemspec +1 -0
- data/sig/ancestor_builder.rbs +1 -1
- data/sig/ast/ruby/annotations.rbs +110 -0
- data/sig/ast/ruby/comment_block.rbs +119 -0
- data/sig/ast/ruby/declarations.rbs +60 -0
- data/sig/ast/ruby/helpers/constant_helper.rbs +11 -0
- data/sig/ast/ruby/helpers/location_helper.rbs +15 -0
- data/sig/ast/ruby/members.rbs +72 -0
- data/sig/buffer.rbs +63 -5
- data/sig/definition.rbs +1 -0
- data/sig/definition_builder.rbs +1 -1
- data/sig/environment/class_entry.rbs +50 -0
- data/sig/environment/module_entry.rbs +50 -0
- data/sig/environment.rbs +28 -133
- data/sig/errors.rbs +13 -6
- data/sig/inline_parser/comment_association.rbs +71 -0
- data/sig/inline_parser.rbs +87 -0
- data/sig/location.rbs +32 -7
- data/sig/manifest.yaml +1 -0
- data/sig/method_builder.rbs +7 -4
- data/sig/parser.rbs +16 -20
- data/sig/resolver/type_name_resolver.rbs +7 -38
- data/sig/source.rbs +48 -0
- data/sig/types.rbs +1 -4
- data/src/ast.c +290 -201
- data/src/lexer.c +2813 -2902
- data/src/lexer.re +4 -0
- data/src/lexstate.c +155 -169
- data/src/location.c +40 -40
- data/src/parser.c +2665 -2433
- data/src/string.c +48 -0
- data/src/util/rbs_allocator.c +76 -92
- data/src/util/rbs_assert.c +10 -10
- data/src/util/rbs_buffer.c +2 -2
- data/src/util/rbs_constant_pool.c +15 -13
- data/src/util/rbs_encoding.c +4062 -20097
- data/src/util/rbs_unescape.c +48 -85
- data/stdlib/bigdecimal/0/big_decimal.rbs +82 -100
- data/stdlib/bigdecimal-math/0/big_math.rbs +8 -169
- data/stdlib/cgi/0/core.rbs +393 -9
- data/stdlib/cgi/0/manifest.yaml +0 -1
- data/stdlib/coverage/0/coverage.rbs +1 -3
- data/stdlib/date/0/date.rbs +59 -67
- data/stdlib/date/0/date_time.rbs +1 -1
- data/stdlib/delegate/0/delegator.rbs +7 -10
- data/stdlib/erb/0/erb.rbs +347 -737
- data/stdlib/fileutils/0/fileutils.rbs +13 -18
- data/stdlib/forwardable/0/forwardable.rbs +0 -3
- data/stdlib/json/0/json.rbs +48 -68
- data/stdlib/net-http/0/net-http.rbs +0 -3
- data/stdlib/objspace/0/objspace.rbs +4 -9
- data/stdlib/open-uri/0/open-uri.rbs +0 -40
- data/stdlib/openssl/0/openssl.rbs +228 -331
- data/stdlib/optparse/0/optparse.rbs +3 -3
- data/stdlib/pathname/0/pathname.rbs +1380 -10
- data/stdlib/psych/0/psych.rbs +3 -3
- data/stdlib/rdoc/0/rdoc.rbs +1 -1
- data/stdlib/resolv/0/resolv.rbs +68 -25
- data/stdlib/ripper/0/ripper.rbs +2 -5
- data/stdlib/singleton/0/singleton.rbs +0 -3
- data/stdlib/socket/0/socket.rbs +1 -13
- data/stdlib/socket/0/tcp_socket.rbs +2 -10
- data/stdlib/stringio/0/stringio.rbs +85 -1176
- data/stdlib/strscan/0/string_scanner.rbs +31 -31
- data/stdlib/tempfile/0/tempfile.rbs +3 -3
- data/stdlib/time/0/time.rbs +1 -1
- data/stdlib/timeout/0/timeout.rbs +7 -63
- data/stdlib/tsort/0/cyclic.rbs +0 -3
- data/stdlib/uri/0/common.rbs +2 -11
- data/stdlib/uri/0/file.rbs +1 -1
- data/stdlib/uri/0/generic.rbs +16 -17
- data/stdlib/uri/0/rfc2396_parser.rbs +7 -6
- data/stdlib/zlib/0/zstream.rbs +0 -1
- metadata +39 -12
- data/.clang-format +0 -74
- data/.clangd +0 -2
- data/.github/workflows/c-check.yml +0 -54
- data/core/pathname.rbs +0 -1272
- data/core/ruby.rbs +0 -53
- data/docs/aliases.md +0 -79
- data/docs/encoding.md +0 -56
- data/ext/rbs_extension/compat.h +0 -10
- data/stdlib/cgi-escape/0/escape.rbs +0 -171
data/core/ractor.rbs
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
# <!-- rdoc-file=ractor.rb -->
|
|
2
|
-
# Ractor
|
|
2
|
+
# Ractor is an Actor-model abstraction for Ruby that provides thread-safe
|
|
3
|
+
# parallel execution.
|
|
4
|
+
#
|
|
5
|
+
# Ractor.new makes a new Ractor, which can run in parallel.
|
|
3
6
|
#
|
|
4
7
|
# # The simplest ractor
|
|
5
8
|
# r = Ractor.new {puts "I am in Ractor!"}
|
|
6
|
-
# r.
|
|
9
|
+
# r.take # wait for it to finish
|
|
7
10
|
# # Here, "I am in Ractor!" is printed
|
|
8
11
|
#
|
|
9
12
|
# Ractors do not share all objects with each other. There are two main benefits
|
|
10
13
|
# to this: across ractors, thread-safety concerns such as data-races and
|
|
11
14
|
# race-conditions are not possible. The other benefit is parallelism.
|
|
12
15
|
#
|
|
13
|
-
# To achieve this, object sharing is limited across ractors.
|
|
14
|
-
# ractors can't access all the objects available in other ractors.
|
|
15
|
-
# objects normally available through variables in the outer scope are
|
|
16
|
-
# from being used across ractors.
|
|
16
|
+
# To achieve this, object sharing is limited across ractors. For example, unlike
|
|
17
|
+
# in threads, ractors can't access all the objects available in other ractors.
|
|
18
|
+
# Even objects normally available through variables in the outer scope are
|
|
19
|
+
# prohibited from being used across ractors.
|
|
17
20
|
#
|
|
18
21
|
# a = 1
|
|
19
22
|
# r = Ractor.new {puts "I am in Ractor! a=#{a}"}
|
|
@@ -24,37 +27,80 @@
|
|
|
24
27
|
# a = 1
|
|
25
28
|
# r = Ractor.new(a) { |a1| puts "I am in Ractor! a=#{a1}"}
|
|
26
29
|
#
|
|
27
|
-
# On CRuby (the default implementation),
|
|
28
|
-
#
|
|
29
|
-
# situation with threads on CRuby.
|
|
30
|
+
# On CRuby (the default implementation), Global Virtual Machine Lock (GVL) is
|
|
31
|
+
# held per ractor, so ractors can perform in parallel without locking each
|
|
32
|
+
# other. This is unlike the situation with threads on CRuby.
|
|
30
33
|
#
|
|
31
34
|
# Instead of accessing shared state, objects should be passed to and from
|
|
32
35
|
# ractors by sending and receiving them as messages.
|
|
33
36
|
#
|
|
34
37
|
# a = 1
|
|
35
38
|
# r = Ractor.new do
|
|
36
|
-
# a_in_ractor = receive # receive blocks
|
|
39
|
+
# a_in_ractor = receive # receive blocks until somebody passes a message
|
|
37
40
|
# puts "I am in Ractor! a=#{a_in_ractor}"
|
|
38
41
|
# end
|
|
39
42
|
# r.send(a) # pass it
|
|
40
|
-
# r.
|
|
43
|
+
# r.take
|
|
41
44
|
# # Here, "I am in Ractor! a=1" is printed
|
|
42
45
|
#
|
|
46
|
+
# There are two pairs of methods for sending/receiving messages:
|
|
47
|
+
#
|
|
48
|
+
# * Ractor#send and Ractor.receive for when the *sender* knows the receiver
|
|
49
|
+
# (push);
|
|
50
|
+
# * Ractor.yield and Ractor#take for when the *receiver* knows the sender
|
|
51
|
+
# (pull);
|
|
52
|
+
#
|
|
43
53
|
# In addition to that, any arguments passed to Ractor.new are passed to the
|
|
44
54
|
# block and available there as if received by Ractor.receive, and the last block
|
|
45
|
-
# value
|
|
55
|
+
# value is sent outside of the ractor as if sent by Ractor.yield.
|
|
56
|
+
#
|
|
57
|
+
# A little demonstration of a classic ping-pong:
|
|
58
|
+
#
|
|
59
|
+
# server = Ractor.new(name: "server") do
|
|
60
|
+
# puts "Server starts: #{self.inspect}"
|
|
61
|
+
# puts "Server sends: ping"
|
|
62
|
+
# Ractor.yield 'ping' # The server doesn't know the receiver and sends to whoever interested
|
|
63
|
+
# received = Ractor.receive # The server doesn't know the sender and receives from whoever sent
|
|
64
|
+
# puts "Server received: #{received}"
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# client = Ractor.new(server) do |srv| # The server is sent to the client, and available as srv
|
|
68
|
+
# puts "Client starts: #{self.inspect}"
|
|
69
|
+
# received = srv.take # The client takes a message from the server
|
|
70
|
+
# puts "Client received from " \
|
|
71
|
+
# "#{srv.inspect}: #{received}"
|
|
72
|
+
# puts "Client sends to " \
|
|
73
|
+
# "#{srv.inspect}: pong"
|
|
74
|
+
# srv.send 'pong' # The client sends a message to the server
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# [client, server].each(&:take) # Wait until they both finish
|
|
78
|
+
#
|
|
79
|
+
# This will output something like:
|
|
80
|
+
#
|
|
81
|
+
# Server starts: #<Ractor:#2 server test.rb:1 running>
|
|
82
|
+
# Server sends: ping
|
|
83
|
+
# Client starts: #<Ractor:#3 test.rb:8 running>
|
|
84
|
+
# Client received from #<Ractor:#2 server test.rb:1 blocking>: ping
|
|
85
|
+
# Client sends to #<Ractor:#2 server test.rb:1 blocking>: pong
|
|
86
|
+
# Server received: pong
|
|
87
|
+
#
|
|
88
|
+
# Ractors receive their messages via the *incoming port*, and send them to the
|
|
89
|
+
# *outgoing port*. Either one can be disabled with Ractor#close_incoming and
|
|
90
|
+
# Ractor#close_outgoing, respectively. When a ractor terminates, its ports are
|
|
91
|
+
# closed automatically.
|
|
46
92
|
#
|
|
47
93
|
# ## Shareable and unshareable objects
|
|
48
94
|
#
|
|
49
|
-
# When an object is sent to a ractor, it's important to understand
|
|
50
|
-
# object is shareable or unshareable. Most Ruby objects are
|
|
51
|
-
# Even frozen objects can be unshareable if they contain
|
|
52
|
-
# variables) unfrozen objects.
|
|
95
|
+
# When an object is sent to and from a ractor, it's important to understand
|
|
96
|
+
# whether the object is shareable or unshareable. Most Ruby objects are
|
|
97
|
+
# unshareable objects. Even frozen objects can be unshareable if they contain
|
|
98
|
+
# (through their instance variables) unfrozen objects.
|
|
53
99
|
#
|
|
54
|
-
# Shareable objects are those which can be used by several
|
|
55
|
-
#
|
|
100
|
+
# Shareable objects are those which can be used by several threads without
|
|
101
|
+
# compromising thread-safety, for example numbers, `true` and `false`.
|
|
56
102
|
# Ractor.shareable? allows you to check this, and Ractor.make_shareable tries to
|
|
57
|
-
# make the object shareable if it's not already and gives an error if it can't
|
|
103
|
+
# make the object shareable if it's not already, and gives an error if it can't
|
|
58
104
|
# do it.
|
|
59
105
|
#
|
|
60
106
|
# Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are shareable
|
|
@@ -70,25 +116,25 @@
|
|
|
70
116
|
# ary[0].frozen? #=> true
|
|
71
117
|
# ary[1].frozen? #=> true
|
|
72
118
|
#
|
|
73
|
-
# When a shareable object is sent via #send, no additional
|
|
74
|
-
#
|
|
75
|
-
# it can be either *copied* or *moved*.
|
|
76
|
-
# the object fully by deep cloning (Object#clone) the
|
|
77
|
-
# structure.
|
|
119
|
+
# When a shareable object is sent (via #send or Ractor.yield), no additional
|
|
120
|
+
# processing occurs on it. It just becomes usable by both ractors. When an
|
|
121
|
+
# unshareable object is sent, it can be either *copied* or *moved*. The first is
|
|
122
|
+
# the default, and it copies the object fully by deep cloning (Object#clone) the
|
|
123
|
+
# non-shareable parts of its structure.
|
|
78
124
|
#
|
|
79
|
-
# data = ['foo'
|
|
125
|
+
# data = ['foo', 'bar'.freeze]
|
|
80
126
|
# r = Ractor.new do
|
|
81
127
|
# data2 = Ractor.receive
|
|
82
128
|
# puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}"
|
|
83
129
|
# end
|
|
84
130
|
# r.send(data)
|
|
85
|
-
# r.
|
|
131
|
+
# r.take
|
|
86
132
|
# puts "Outside : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
|
|
87
133
|
#
|
|
88
134
|
# This will output something like:
|
|
89
135
|
#
|
|
90
|
-
# In ractor:
|
|
91
|
-
# Outside :
|
|
136
|
+
# In ractor: 340, 360, 320
|
|
137
|
+
# Outside : 380, 400, 320
|
|
92
138
|
#
|
|
93
139
|
# Note that the object ids of the array and the non-frozen string inside the
|
|
94
140
|
# array have changed in the ractor because they are different objects. The
|
|
@@ -105,7 +151,7 @@
|
|
|
105
151
|
# puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}"
|
|
106
152
|
# end
|
|
107
153
|
# r.send(data, move: true)
|
|
108
|
-
# r.
|
|
154
|
+
# r.take
|
|
109
155
|
# puts "Outside: moved? #{Ractor::MovedObject === data}"
|
|
110
156
|
# puts "Outside: #{data.inspect}"
|
|
111
157
|
#
|
|
@@ -115,14 +161,14 @@
|
|
|
115
161
|
# Outside: moved? true
|
|
116
162
|
# test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
|
|
117
163
|
#
|
|
118
|
-
# Notice that even `inspect` and more basic methods like `__id__`
|
|
164
|
+
# Notice that even `inspect` (and more basic methods like `__id__`) is
|
|
119
165
|
# inaccessible on a moved object.
|
|
120
166
|
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
# shareable objects in Ruby
|
|
125
|
-
# them.
|
|
167
|
+
# Class and Module objects are shareable so the class/module definitions are
|
|
168
|
+
# shared between ractors. Ractor objects are also shareable. All operations on
|
|
169
|
+
# shareable objects are thread-safe, so the thread-safety property will be kept.
|
|
170
|
+
# We can not define mutable shareable objects in Ruby, but C extensions can
|
|
171
|
+
# introduce them.
|
|
126
172
|
#
|
|
127
173
|
# It is prohibited to access (get) instance variables of shareable objects in
|
|
128
174
|
# other ractors if the values of the variables aren't shareable. This can occur
|
|
@@ -143,7 +189,7 @@
|
|
|
143
189
|
# puts "I can't see #{cls.tricky}"
|
|
144
190
|
# cls.tricky = true # doesn't get here, but this would also raise an error
|
|
145
191
|
# end
|
|
146
|
-
# r.
|
|
192
|
+
# r.take
|
|
147
193
|
# # I see C
|
|
148
194
|
# # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
|
|
149
195
|
#
|
|
@@ -157,7 +203,7 @@
|
|
|
157
203
|
# puts "GOOD=#{GOOD}"
|
|
158
204
|
# puts "BAD=#{BAD}"
|
|
159
205
|
# end
|
|
160
|
-
# r.
|
|
206
|
+
# r.take
|
|
161
207
|
# # GOOD=good
|
|
162
208
|
# # can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError)
|
|
163
209
|
#
|
|
@@ -167,7 +213,7 @@
|
|
|
167
213
|
# puts "I see #{C}"
|
|
168
214
|
# puts "I can't see #{C.tricky}"
|
|
169
215
|
# end
|
|
170
|
-
# r.
|
|
216
|
+
# r.take
|
|
171
217
|
# # I see C
|
|
172
218
|
# # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
|
|
173
219
|
#
|
|
@@ -183,21 +229,20 @@
|
|
|
183
229
|
# a = 1
|
|
184
230
|
# Thread.new {puts "Thread in ractor: a=#{a}"}.join
|
|
185
231
|
# end
|
|
186
|
-
# r.
|
|
232
|
+
# r.take
|
|
187
233
|
# # Here "Thread in ractor: a=1" will be printed
|
|
188
234
|
#
|
|
189
235
|
# ## Note on code examples
|
|
190
236
|
#
|
|
191
237
|
# In the examples below, sometimes we use the following method to wait for
|
|
192
|
-
# ractors to make progress
|
|
238
|
+
# ractors that are not currently blocked to finish (or to make progress).
|
|
193
239
|
#
|
|
194
240
|
# def wait
|
|
195
241
|
# sleep(0.1)
|
|
196
242
|
# end
|
|
197
243
|
#
|
|
198
|
-
#
|
|
199
|
-
# code. Most of the time, #
|
|
200
|
-
# Ractor.receive is used to wait for messages.
|
|
244
|
+
# It is **only for demonstration purposes** and shouldn't be used in a real
|
|
245
|
+
# code. Most of the time, #take is used to wait for ractors to finish.
|
|
201
246
|
#
|
|
202
247
|
# ## Reference
|
|
203
248
|
#
|
|
@@ -216,7 +261,7 @@ class Ractor
|
|
|
216
261
|
# rdoc-file=ractor.rb
|
|
217
262
|
# - [](sym)
|
|
218
263
|
# -->
|
|
219
|
-
#
|
|
264
|
+
# get a value from ractor-local storage of current Ractor
|
|
220
265
|
#
|
|
221
266
|
def self.[]: (Symbol) -> untyped
|
|
222
267
|
|
|
@@ -224,7 +269,7 @@ class Ractor
|
|
|
224
269
|
# rdoc-file=ractor.rb
|
|
225
270
|
# - []=(sym, val)
|
|
226
271
|
# -->
|
|
227
|
-
#
|
|
272
|
+
# set a value in ractor-local storage of current Ractor
|
|
228
273
|
#
|
|
229
274
|
def self.[]=: (Symbol, untyped) -> untyped
|
|
230
275
|
|
|
@@ -232,13 +277,13 @@ class Ractor
|
|
|
232
277
|
# rdoc-file=ractor.rb
|
|
233
278
|
# - count()
|
|
234
279
|
# -->
|
|
235
|
-
# Returns the number of
|
|
280
|
+
# Returns the number of Ractors currently running or blocking (waiting).
|
|
236
281
|
#
|
|
237
282
|
# Ractor.count #=> 1
|
|
238
|
-
# r = Ractor.new(name: 'example') { Ractor.
|
|
283
|
+
# r = Ractor.new(name: 'example') { Ractor.yield(1) }
|
|
239
284
|
# Ractor.count #=> 2 (main + example ractor)
|
|
240
|
-
# r
|
|
241
|
-
# r.
|
|
285
|
+
# r.take # wait for Ractor.yield(1)
|
|
286
|
+
# r.take # wait until r will finish
|
|
242
287
|
# Ractor.count #=> 1
|
|
243
288
|
#
|
|
244
289
|
def self.count: () -> Integer
|
|
@@ -257,7 +302,7 @@ class Ractor
|
|
|
257
302
|
# rdoc-file=ractor.rb
|
|
258
303
|
# - main()
|
|
259
304
|
# -->
|
|
260
|
-
#
|
|
305
|
+
# returns main ractor
|
|
261
306
|
#
|
|
262
307
|
def self.main: () -> Ractor
|
|
263
308
|
|
|
@@ -265,7 +310,7 @@ class Ractor
|
|
|
265
310
|
# rdoc-file=ractor.rb
|
|
266
311
|
# - main?()
|
|
267
312
|
# -->
|
|
268
|
-
#
|
|
313
|
+
# return true if the current ractor is main ractor
|
|
269
314
|
#
|
|
270
315
|
def self.main?: () -> boolish
|
|
271
316
|
|
|
@@ -273,7 +318,7 @@ class Ractor
|
|
|
273
318
|
# rdoc-file=ractor.rb
|
|
274
319
|
# - Ractor.make_shareable(obj, copy: false) -> shareable_obj
|
|
275
320
|
# -->
|
|
276
|
-
#
|
|
321
|
+
# Make `obj` shareable between ractors.
|
|
277
322
|
#
|
|
278
323
|
# `obj` and all the objects it refers to will be frozen, unless they are already
|
|
279
324
|
# shareable.
|
|
@@ -312,13 +357,13 @@ class Ractor
|
|
|
312
357
|
# rdoc-file=ractor.rb
|
|
313
358
|
# - Ractor.new(*args, name: nil) {|*args| block } -> ractor
|
|
314
359
|
# -->
|
|
315
|
-
#
|
|
360
|
+
# Create a new Ractor with args and a block.
|
|
316
361
|
#
|
|
317
|
-
# The given block (Proc)
|
|
318
|
-
# inside the block will refer to the current Ractor.
|
|
362
|
+
# The given block (Proc) will be isolated (can't access any outer variables).
|
|
363
|
+
# `self` inside the block will refer to the current Ractor.
|
|
319
364
|
#
|
|
320
365
|
# r = Ractor.new { puts "Hi, I am #{self.inspect}" }
|
|
321
|
-
# r.
|
|
366
|
+
# r.take
|
|
322
367
|
# # Prints "Hi, I am #<Ractor:#2 test.rb:1 running>"
|
|
323
368
|
#
|
|
324
369
|
# Any `args` passed are propagated to the block arguments by the same rules as
|
|
@@ -330,14 +375,14 @@ class Ractor
|
|
|
330
375
|
# r = Ractor.new(arg) {|received_arg|
|
|
331
376
|
# puts "Received: #{received_arg} (##{received_arg.object_id})"
|
|
332
377
|
# }
|
|
333
|
-
# r.
|
|
378
|
+
# r.take
|
|
334
379
|
# # Prints:
|
|
335
380
|
# # Passing: [1, 2, 3] (#280)
|
|
336
381
|
# # Received: [1, 2, 3] (#300)
|
|
337
382
|
#
|
|
338
383
|
# Ractor's `name` can be set for debugging purposes:
|
|
339
384
|
#
|
|
340
|
-
# r = Ractor.new(name: 'my ractor') {}; r.
|
|
385
|
+
# r = Ractor.new(name: 'my ractor') {}; r.take
|
|
341
386
|
# p r
|
|
342
387
|
# #=> #<Ractor:#3 my ractor test.rb:1 terminated>
|
|
343
388
|
#
|
|
@@ -345,12 +390,128 @@ class Ractor
|
|
|
345
390
|
|
|
346
391
|
# <!--
|
|
347
392
|
# rdoc-file=ractor.rb
|
|
348
|
-
# - Ractor.receive ->
|
|
393
|
+
# - Ractor.receive -> msg
|
|
349
394
|
# -->
|
|
350
|
-
#
|
|
395
|
+
# Receive a message from the incoming port of the current ractor (which was sent
|
|
396
|
+
# there by #send from another ractor).
|
|
397
|
+
#
|
|
398
|
+
# r = Ractor.new do
|
|
399
|
+
# v1 = Ractor.receive
|
|
400
|
+
# puts "Received: #{v1}"
|
|
401
|
+
# end
|
|
402
|
+
# r.send('message1')
|
|
403
|
+
# r.take
|
|
404
|
+
# # Here will be printed: "Received: message1"
|
|
405
|
+
#
|
|
406
|
+
# Alternatively, the private instance method `receive` may be used:
|
|
407
|
+
#
|
|
408
|
+
# r = Ractor.new do
|
|
409
|
+
# v1 = receive
|
|
410
|
+
# puts "Received: #{v1}"
|
|
411
|
+
# end
|
|
412
|
+
# r.send('message1')
|
|
413
|
+
# r.take
|
|
414
|
+
# # This prints: "Received: message1"
|
|
415
|
+
#
|
|
416
|
+
# The method blocks if the queue is empty.
|
|
417
|
+
#
|
|
418
|
+
# r = Ractor.new do
|
|
419
|
+
# puts "Before first receive"
|
|
420
|
+
# v1 = Ractor.receive
|
|
421
|
+
# puts "Received: #{v1}"
|
|
422
|
+
# v2 = Ractor.receive
|
|
423
|
+
# puts "Received: #{v2}"
|
|
424
|
+
# end
|
|
425
|
+
# wait
|
|
426
|
+
# puts "Still not received"
|
|
427
|
+
# r.send('message1')
|
|
428
|
+
# wait
|
|
429
|
+
# puts "Still received only one"
|
|
430
|
+
# r.send('message2')
|
|
431
|
+
# r.take
|
|
432
|
+
#
|
|
433
|
+
# Output:
|
|
434
|
+
#
|
|
435
|
+
# Before first receive
|
|
436
|
+
# Still not received
|
|
437
|
+
# Received: message1
|
|
438
|
+
# Still received only one
|
|
439
|
+
# Received: message2
|
|
440
|
+
#
|
|
441
|
+
# If close_incoming was called on the ractor, the method raises
|
|
442
|
+
# Ractor::ClosedError if there are no more messages in the incoming queue:
|
|
443
|
+
#
|
|
444
|
+
# Ractor.new do
|
|
445
|
+
# close_incoming
|
|
446
|
+
# receive
|
|
447
|
+
# end
|
|
448
|
+
# wait
|
|
449
|
+
# # in `receive': The incoming port is already closed => #<Ractor:#2 test.rb:1 running> (Ractor::ClosedError)
|
|
351
450
|
#
|
|
352
451
|
def self.receive: () -> untyped
|
|
353
452
|
|
|
453
|
+
# <!--
|
|
454
|
+
# rdoc-file=ractor.rb
|
|
455
|
+
# - Ractor.receive_if {|msg| block } -> msg
|
|
456
|
+
# -->
|
|
457
|
+
# Receive only a specific message.
|
|
458
|
+
#
|
|
459
|
+
# Instead of Ractor.receive, Ractor.receive_if can be given a pattern (or any
|
|
460
|
+
# filter) in a block and you can choose the messages to accept that are
|
|
461
|
+
# available in your ractor's incoming queue.
|
|
462
|
+
#
|
|
463
|
+
# r = Ractor.new do
|
|
464
|
+
# p Ractor.receive_if{|msg| msg.match?(/foo/)} #=> "foo3"
|
|
465
|
+
# p Ractor.receive_if{|msg| msg.match?(/bar/)} #=> "bar1"
|
|
466
|
+
# p Ractor.receive_if{|msg| msg.match?(/baz/)} #=> "baz2"
|
|
467
|
+
# end
|
|
468
|
+
# r << "bar1"
|
|
469
|
+
# r << "baz2"
|
|
470
|
+
# r << "foo3"
|
|
471
|
+
# r.take
|
|
472
|
+
#
|
|
473
|
+
# This will output:
|
|
474
|
+
#
|
|
475
|
+
# foo3
|
|
476
|
+
# bar1
|
|
477
|
+
# baz2
|
|
478
|
+
#
|
|
479
|
+
# If the block returns a truthy value, the message is removed from the incoming
|
|
480
|
+
# queue and returned. Otherwise, the message remains in the incoming queue and
|
|
481
|
+
# the next messages are checked by the given block.
|
|
482
|
+
#
|
|
483
|
+
# If there are no messages left in the incoming queue, the method will block
|
|
484
|
+
# until new messages arrive.
|
|
485
|
+
#
|
|
486
|
+
# If the block is escaped by break/return/exception/throw, the message is
|
|
487
|
+
# removed from the incoming queue as if a truthy value had been returned.
|
|
488
|
+
#
|
|
489
|
+
# r = Ractor.new do
|
|
490
|
+
# val = Ractor.receive_if{|msg| msg.is_a?(Array)}
|
|
491
|
+
# puts "Received successfully: #{val}"
|
|
492
|
+
# end
|
|
493
|
+
#
|
|
494
|
+
# r.send(1)
|
|
495
|
+
# r.send('test')
|
|
496
|
+
# wait
|
|
497
|
+
# puts "2 non-matching sent, nothing received"
|
|
498
|
+
# r.send([1, 2, 3])
|
|
499
|
+
# wait
|
|
500
|
+
#
|
|
501
|
+
# Prints:
|
|
502
|
+
#
|
|
503
|
+
# 2 non-matching sent, nothing received
|
|
504
|
+
# Received successfully: [1, 2, 3]
|
|
505
|
+
#
|
|
506
|
+
# Note that you can not call receive/receive_if in the given block recursively.
|
|
507
|
+
# You should not do any tasks in the block other than message filtration.
|
|
508
|
+
#
|
|
509
|
+
# Ractor.current << true
|
|
510
|
+
# Ractor.receive_if{|msg| Ractor.receive}
|
|
511
|
+
# #=> `receive': can not call receive/receive_if recursively (Ractor::Error)
|
|
512
|
+
#
|
|
513
|
+
def self.receive_if: () { (untyped) -> boolish } -> untyped
|
|
514
|
+
|
|
354
515
|
# <!--
|
|
355
516
|
# rdoc-file=ractor.rb
|
|
356
517
|
# - recv()
|
|
@@ -360,50 +521,57 @@ class Ractor
|
|
|
360
521
|
|
|
361
522
|
# <!--
|
|
362
523
|
# rdoc-file=ractor.rb
|
|
363
|
-
# - Ractor.select(*
|
|
524
|
+
# - Ractor.select(*ractors, [yield_value:, move: false]) -> [ractor or symbol, obj]
|
|
364
525
|
# -->
|
|
365
|
-
#
|
|
366
|
-
#
|
|
367
|
-
#
|
|
368
|
-
#
|
|
369
|
-
#
|
|
370
|
-
#
|
|
371
|
-
#
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
# sleep rand(0.99)
|
|
377
|
-
# p.send("r#{i}")
|
|
378
|
-
# sleep rand(0.99)
|
|
379
|
-
# "r#{i} done"
|
|
380
|
-
# end
|
|
381
|
-
# end
|
|
526
|
+
# Wait for any ractor to have something in its outgoing port, read from this
|
|
527
|
+
# ractor, and then return that ractor and the object received.
|
|
528
|
+
#
|
|
529
|
+
# r1 = Ractor.new {Ractor.yield 'from 1'}
|
|
530
|
+
# r2 = Ractor.new {Ractor.yield 'from 2'}
|
|
531
|
+
#
|
|
532
|
+
# r, obj = Ractor.select(r1, r2)
|
|
533
|
+
#
|
|
534
|
+
# puts "received #{obj.inspect} from #{r.inspect}"
|
|
535
|
+
# # Prints: received "from 1" from #<Ractor:#2 test.rb:1 running>
|
|
536
|
+
# # But could just as well print "from r2" here, either prints could be first.
|
|
382
537
|
#
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
#
|
|
386
|
-
#
|
|
387
|
-
#
|
|
538
|
+
# If one of the given ractors is the current ractor, and it is selected, `r`
|
|
539
|
+
# will contain the `:receive` symbol instead of the ractor object.
|
|
540
|
+
#
|
|
541
|
+
# r1 = Ractor.new(Ractor.current) do |main|
|
|
542
|
+
# main.send 'to main'
|
|
543
|
+
# Ractor.yield 'from 1'
|
|
544
|
+
# end
|
|
545
|
+
# r2 = Ractor.new do
|
|
546
|
+
# Ractor.yield 'from 2'
|
|
388
547
|
# end
|
|
389
548
|
#
|
|
390
|
-
#
|
|
391
|
-
# #
|
|
392
|
-
# #
|
|
393
|
-
# # r0 done
|
|
549
|
+
# r, obj = Ractor.select(r1, r2, Ractor.current)
|
|
550
|
+
# puts "received #{obj.inspect} from #{r.inspect}"
|
|
551
|
+
# # Could print: received "to main" from :receive
|
|
394
552
|
#
|
|
395
|
-
#
|
|
396
|
-
#
|
|
397
|
-
# waiting for their termination in the array element order.
|
|
553
|
+
# If `yield_value` is provided, that value may be yielded if another ractor is
|
|
554
|
+
# calling #take. In this case, the pair `[:yield, nil]` is returned:
|
|
398
555
|
#
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
# r, val = Ractor.select(*ractors)
|
|
402
|
-
# ractors.delete(r)
|
|
403
|
-
# values << val
|
|
556
|
+
# r1 = Ractor.new(Ractor.current) do |main|
|
|
557
|
+
# puts "Received from main: #{main.take}"
|
|
404
558
|
# end
|
|
405
559
|
#
|
|
406
|
-
|
|
560
|
+
# puts "Trying to select"
|
|
561
|
+
# r, obj = Ractor.select(r1, Ractor.current, yield_value: 123)
|
|
562
|
+
# wait
|
|
563
|
+
# puts "Received #{obj.inspect} from #{r.inspect}"
|
|
564
|
+
#
|
|
565
|
+
# This will print:
|
|
566
|
+
#
|
|
567
|
+
# Trying to select
|
|
568
|
+
# Received from main: 123
|
|
569
|
+
# Received nil from :yield
|
|
570
|
+
#
|
|
571
|
+
# `move` boolean flag defines whether yielded value will be copied (default) or
|
|
572
|
+
# moved.
|
|
573
|
+
#
|
|
574
|
+
def self.select: (*Ractor ractors, ?move: boolish, ?yield_value: untyped) -> [ Ractor | Symbol, untyped ]
|
|
407
575
|
|
|
408
576
|
# <!--
|
|
409
577
|
# rdoc-file=ractor.rb
|
|
@@ -411,7 +579,7 @@ class Ractor
|
|
|
411
579
|
# -->
|
|
412
580
|
# Checks if the object is shareable by ractors.
|
|
413
581
|
#
|
|
414
|
-
# Ractor.shareable?(1) #=> true -- numbers are
|
|
582
|
+
# Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are frozen
|
|
415
583
|
# Ractor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true
|
|
416
584
|
# Ractor.shareable?('foo'.freeze) #=> true
|
|
417
585
|
#
|
|
@@ -420,44 +588,13 @@ class Ractor
|
|
|
420
588
|
#
|
|
421
589
|
def self.shareable?: (untyped obj) -> bool
|
|
422
590
|
|
|
423
|
-
# <!--
|
|
424
|
-
# rdoc-file=ractor.rb
|
|
425
|
-
# - Ractor.shareable_proc(self: nil){} -> shareable proc
|
|
426
|
-
# -->
|
|
427
|
-
# Returns a shareable copy of the given block's Proc. The value of `self` in the
|
|
428
|
-
# Proc will be replaced with the value passed via the `self:` keyword, or `nil`
|
|
429
|
-
# if not given.
|
|
430
|
-
#
|
|
431
|
-
# In a shareable Proc, access to any outer variables if prohibited.
|
|
432
|
-
#
|
|
433
|
-
# a = 42
|
|
434
|
-
# Ractor.shareable_proc{ p a }
|
|
435
|
-
# #=> can not isolate a Proc because it accesses outer variables (a). (ArgumentError)
|
|
436
|
-
#
|
|
437
|
-
# The value of `self` in the Proc must be a shareable object.
|
|
438
|
-
#
|
|
439
|
-
# Ractor.shareable_proc(self: self){}
|
|
440
|
-
# #=> self should be shareable: main (Ractor::IsolationError)
|
|
441
|
-
#
|
|
442
|
-
def self.shareable_proc: [T] () { (?) [self: nil] -> T } -> ^(?) [self: nil] -> T
|
|
443
|
-
| [T, S] (self: S) { (?) [self: S] -> T } -> ^(?) [self: S] -> T
|
|
444
|
-
|
|
445
|
-
# <!--
|
|
446
|
-
# rdoc-file=ractor.rb
|
|
447
|
-
# - Ractor.shareable_lambda(self: nil){} -> shareable lambda
|
|
448
|
-
# -->
|
|
449
|
-
# Same as Ractor.shareable_proc, but returns a lambda Proc.
|
|
450
|
-
#
|
|
451
|
-
def self.shareable_lambda: [T] () { (?) [self: nil] -> T } -> ^(?) [self: nil] -> T
|
|
452
|
-
| [T, S] (self: S) { (?) [self: S] -> T } -> ^(?) [self: S] -> T
|
|
453
|
-
|
|
454
591
|
# <!--
|
|
455
592
|
# rdoc-file=ractor.rb
|
|
456
593
|
# - Ractor.store_if_absent(key){ init_block }
|
|
457
594
|
# -->
|
|
458
|
-
# If the
|
|
459
|
-
#
|
|
460
|
-
#
|
|
595
|
+
# If the correponding value is not set, yield a value with init_block and store
|
|
596
|
+
# the value in thread-safe manner. This method returns corresponding stored
|
|
597
|
+
# value.
|
|
461
598
|
#
|
|
462
599
|
# (1..10).map{
|
|
463
600
|
# Thread.new(it){|i|
|
|
@@ -470,7 +607,47 @@ class Ractor
|
|
|
470
607
|
|
|
471
608
|
# <!--
|
|
472
609
|
# rdoc-file=ractor.rb
|
|
473
|
-
# -
|
|
610
|
+
# - Ractor.yield(msg, move: false) -> nil
|
|
611
|
+
# -->
|
|
612
|
+
# Send a message to the current ractor's outgoing port to be accepted by #take.
|
|
613
|
+
#
|
|
614
|
+
# r = Ractor.new {Ractor.yield 'Hello from ractor'}
|
|
615
|
+
# puts r.take
|
|
616
|
+
# # Prints: "Hello from ractor"
|
|
617
|
+
#
|
|
618
|
+
# This method is blocking, and will return only when somebody consumes the sent
|
|
619
|
+
# message.
|
|
620
|
+
#
|
|
621
|
+
# r = Ractor.new do
|
|
622
|
+
# Ractor.yield 'Hello from ractor'
|
|
623
|
+
# puts "Ractor: after yield"
|
|
624
|
+
# end
|
|
625
|
+
# wait
|
|
626
|
+
# puts "Still not taken"
|
|
627
|
+
# puts r.take
|
|
628
|
+
#
|
|
629
|
+
# This will print:
|
|
630
|
+
#
|
|
631
|
+
# Still not taken
|
|
632
|
+
# Hello from ractor
|
|
633
|
+
# Ractor: after yield
|
|
634
|
+
#
|
|
635
|
+
# If the outgoing port was closed with #close_outgoing, the method will raise:
|
|
636
|
+
#
|
|
637
|
+
# r = Ractor.new do
|
|
638
|
+
# close_outgoing
|
|
639
|
+
# Ractor.yield 'Hello from ractor'
|
|
640
|
+
# end
|
|
641
|
+
# wait
|
|
642
|
+
# # `yield': The outgoing-port is already closed (Ractor::ClosedError)
|
|
643
|
+
#
|
|
644
|
+
# The meaning of the `move` argument is the same as for #send.
|
|
645
|
+
#
|
|
646
|
+
def self.yield: (untyped obj, ?move: boolish) -> untyped
|
|
647
|
+
|
|
648
|
+
# <!--
|
|
649
|
+
# rdoc-file=ractor.rb
|
|
650
|
+
# - <<(obj, move: false)
|
|
474
651
|
# -->
|
|
475
652
|
#
|
|
476
653
|
alias << send
|
|
@@ -479,117 +656,225 @@ class Ractor
|
|
|
479
656
|
# rdoc-file=ractor.rb
|
|
480
657
|
# - [](sym)
|
|
481
658
|
# -->
|
|
482
|
-
#
|
|
659
|
+
# get a value from ractor-local storage of current Ractor Obsolete and use
|
|
483
660
|
# Ractor.[] instead.
|
|
484
661
|
#
|
|
485
|
-
%a{deprecated: Use Ractor.[] instead}
|
|
486
662
|
def []: (interned sym) -> untyped
|
|
487
663
|
|
|
488
664
|
# <!--
|
|
489
665
|
# rdoc-file=ractor.rb
|
|
490
666
|
# - []=(sym, val)
|
|
491
667
|
# -->
|
|
492
|
-
#
|
|
668
|
+
# set a value in ractor-local storage of current Ractor Obsolete and use
|
|
493
669
|
# Ractor.[]= instead.
|
|
494
670
|
#
|
|
495
|
-
%a{deprecated: Use Ractor.[]= instead}
|
|
496
671
|
def []=: [T] (interned sym, T val) -> T
|
|
497
672
|
|
|
498
673
|
# <!--
|
|
499
674
|
# rdoc-file=ractor.rb
|
|
500
|
-
# - ractor.
|
|
675
|
+
# - ractor.close_incoming -> true | false
|
|
501
676
|
# -->
|
|
502
|
-
#
|
|
677
|
+
# Closes the incoming port and returns whether it was already closed. All
|
|
678
|
+
# further attempts to Ractor.receive in the ractor, and #send to the ractor will
|
|
679
|
+
# fail with Ractor::ClosedError.
|
|
503
680
|
#
|
|
504
|
-
|
|
681
|
+
# r = Ractor.new {sleep(500)}
|
|
682
|
+
# r.close_incoming #=> false
|
|
683
|
+
# r.close_incoming #=> true
|
|
684
|
+
# r.send('test')
|
|
685
|
+
# # Ractor::ClosedError (The incoming-port is already closed)
|
|
686
|
+
#
|
|
687
|
+
def close_incoming: () -> bool
|
|
505
688
|
|
|
506
689
|
# <!--
|
|
507
690
|
# rdoc-file=ractor.rb
|
|
508
|
-
# -
|
|
691
|
+
# - ractor.close_outgoing -> true | false
|
|
509
692
|
# -->
|
|
693
|
+
# Closes the outgoing port and returns whether it was already closed. All
|
|
694
|
+
# further attempts to Ractor.yield in the ractor, and #take from the ractor will
|
|
695
|
+
# fail with Ractor::ClosedError.
|
|
510
696
|
#
|
|
511
|
-
|
|
697
|
+
# r = Ractor.new {sleep(500)}
|
|
698
|
+
# r.close_outgoing #=> false
|
|
699
|
+
# r.close_outgoing #=> true
|
|
700
|
+
# r.take
|
|
701
|
+
# # Ractor::ClosedError (The outgoing-port is already closed)
|
|
702
|
+
#
|
|
703
|
+
def close_outgoing: () -> bool
|
|
512
704
|
|
|
513
705
|
# <!--
|
|
514
706
|
# rdoc-file=ractor.rb
|
|
515
|
-
# -
|
|
707
|
+
# - inspect()
|
|
516
708
|
# -->
|
|
517
|
-
# Waits for the termination of the Ractor. If the Ractor was aborted (terminated
|
|
518
|
-
# by an unhandled exception), the exception is raised in the current ractor.
|
|
519
|
-
#
|
|
520
|
-
# Ractor.new{}.join #=> ractor
|
|
521
709
|
#
|
|
522
|
-
|
|
523
|
-
# #=> raises the exception "foo (RuntimeError)"
|
|
524
|
-
#
|
|
525
|
-
def join: () -> self
|
|
710
|
+
def inspect: () -> String
|
|
526
711
|
|
|
527
712
|
# <!--
|
|
528
713
|
# rdoc-file=ractor.rb
|
|
529
714
|
# - name()
|
|
530
715
|
# -->
|
|
531
|
-
#
|
|
716
|
+
# The name set in Ractor.new, or `nil`.
|
|
532
717
|
#
|
|
533
718
|
def name: () -> String?
|
|
534
719
|
|
|
535
720
|
# <!--
|
|
536
721
|
# rdoc-file=ractor.rb
|
|
537
|
-
# - ractor.
|
|
722
|
+
# - ractor.send(msg, move: false) -> self
|
|
538
723
|
# -->
|
|
539
|
-
#
|
|
540
|
-
# terminates, the port receives a Symbol object.
|
|
724
|
+
# Send a message to a Ractor's incoming queue to be accepted by Ractor.receive.
|
|
541
725
|
#
|
|
542
|
-
#
|
|
543
|
-
#
|
|
726
|
+
# r = Ractor.new do
|
|
727
|
+
# value = Ractor.receive
|
|
728
|
+
# puts "Received #{value}"
|
|
729
|
+
# end
|
|
730
|
+
# r.send 'message'
|
|
731
|
+
# # Prints: "Received: message"
|
|
544
732
|
#
|
|
545
|
-
#
|
|
546
|
-
#
|
|
547
|
-
# port.receive #=> :exited and r is terminated
|
|
733
|
+
# The method is non-blocking (will return immediately even if the ractor is not
|
|
734
|
+
# ready to receive anything):
|
|
548
735
|
#
|
|
549
|
-
#
|
|
550
|
-
#
|
|
551
|
-
#
|
|
736
|
+
# r = Ractor.new {sleep(5)}
|
|
737
|
+
# r.send('test')
|
|
738
|
+
# puts "Sent successfully"
|
|
739
|
+
# # Prints: "Sent successfully" immediately
|
|
552
740
|
#
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
#
|
|
556
|
-
#
|
|
557
|
-
#
|
|
558
|
-
#
|
|
559
|
-
#
|
|
741
|
+
# An attempt to send to a ractor which already finished its execution will raise
|
|
742
|
+
# Ractor::ClosedError.
|
|
743
|
+
#
|
|
744
|
+
# r = Ractor.new {}
|
|
745
|
+
# r.take
|
|
746
|
+
# p r
|
|
747
|
+
# # "#<Ractor:#6 (irb):23 terminated>"
|
|
748
|
+
# r.send('test')
|
|
749
|
+
# # Ractor::ClosedError (The incoming-port is already closed)
|
|
750
|
+
#
|
|
751
|
+
# If close_incoming was called on the ractor, the method also raises
|
|
752
|
+
# Ractor::ClosedError.
|
|
753
|
+
#
|
|
754
|
+
# r = Ractor.new do
|
|
755
|
+
# sleep(500)
|
|
756
|
+
# receive
|
|
757
|
+
# end
|
|
758
|
+
# r.close_incoming
|
|
759
|
+
# r.send('test')
|
|
760
|
+
# # Ractor::ClosedError (The incoming-port is already closed)
|
|
761
|
+
# # The error is raised immediately, not when the ractor tries to receive
|
|
762
|
+
#
|
|
763
|
+
# If the `obj` is unshareable, by default it will be copied into the receiving
|
|
764
|
+
# ractor by deep cloning. If `move: true` is passed, the object is *moved* into
|
|
765
|
+
# the receiving ractor and becomes inaccessible to the sender.
|
|
766
|
+
#
|
|
767
|
+
# r = Ractor.new {puts "Received: #{receive}"}
|
|
768
|
+
# msg = 'message'
|
|
769
|
+
# r.send(msg, move: true)
|
|
770
|
+
# r.take
|
|
771
|
+
# p msg
|
|
772
|
+
#
|
|
773
|
+
# This prints:
|
|
774
|
+
#
|
|
775
|
+
# Received: message
|
|
776
|
+
# in `p': undefined method `inspect' for #<Ractor::MovedObject:0x000055c99b9b69b8>
|
|
777
|
+
#
|
|
778
|
+
# All references to the object and its parts will become invalid to the sender.
|
|
779
|
+
#
|
|
780
|
+
# r = Ractor.new {puts "Received: #{receive}"}
|
|
781
|
+
# s = 'message'
|
|
782
|
+
# ary = [s]
|
|
783
|
+
# copy = ary.dup
|
|
784
|
+
# r.send(ary, move: true)
|
|
785
|
+
#
|
|
786
|
+
# s.inspect
|
|
787
|
+
# # Ractor::MovedError (can not send any methods to a moved object)
|
|
788
|
+
# ary.class
|
|
789
|
+
# # Ractor::MovedError (can not send any methods to a moved object)
|
|
790
|
+
# copy.class
|
|
791
|
+
# # => Array, it is different object
|
|
792
|
+
# copy[0].inspect
|
|
793
|
+
# # Ractor::MovedError (can not send any methods to a moved object)
|
|
794
|
+
# # ...but its item was still a reference to `s`, which was moved
|
|
795
|
+
#
|
|
796
|
+
# If the object is shareable, `move: true` has no effect on it:
|
|
797
|
+
#
|
|
798
|
+
# r = Ractor.new {puts "Received: #{receive}"}
|
|
799
|
+
# s = 'message'.freeze
|
|
800
|
+
# r.send(s, move: true)
|
|
801
|
+
# s.inspect #=> "message", still available
|
|
560
802
|
#
|
|
561
803
|
def send: (untyped obj, ?move: boolish) -> Ractor
|
|
562
804
|
|
|
563
805
|
# <!--
|
|
564
806
|
# rdoc-file=ractor.rb
|
|
565
|
-
# -
|
|
807
|
+
# - ractor.take -> msg
|
|
566
808
|
# -->
|
|
809
|
+
# Get a message from the ractor's outgoing port, which was put there by
|
|
810
|
+
# Ractor.yield or at ractor's termination.
|
|
567
811
|
#
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
573
|
-
#
|
|
574
|
-
#
|
|
812
|
+
# r = Ractor.new do
|
|
813
|
+
# Ractor.yield 'explicit yield'
|
|
814
|
+
# 'last value'
|
|
815
|
+
# end
|
|
816
|
+
# puts r.take #=> 'explicit yield'
|
|
817
|
+
# puts r.take #=> 'last value'
|
|
818
|
+
# puts r.take # Ractor::ClosedError (The outgoing-port is already closed)
|
|
819
|
+
#
|
|
820
|
+
# The fact that the last value is also sent to the outgoing port means that
|
|
821
|
+
# `take` can be used as an analog of Thread#join ("just wait until ractor
|
|
822
|
+
# finishes"). However, it will raise if somebody has already consumed that
|
|
823
|
+
# message.
|
|
824
|
+
#
|
|
825
|
+
# If the outgoing port was closed with #close_outgoing, the method will raise
|
|
826
|
+
# Ractor::ClosedError.
|
|
827
|
+
#
|
|
828
|
+
# r = Ractor.new do
|
|
829
|
+
# sleep(500)
|
|
830
|
+
# Ractor.yield 'Hello from ractor'
|
|
831
|
+
# end
|
|
832
|
+
# r.close_outgoing
|
|
833
|
+
# r.take
|
|
834
|
+
# # Ractor::ClosedError (The outgoing-port is already closed)
|
|
835
|
+
# # The error would be raised immediately, not when ractor will try to receive
|
|
836
|
+
#
|
|
837
|
+
# If an uncaught exception is raised in the Ractor, it is propagated by take as
|
|
838
|
+
# a Ractor::RemoteError.
|
|
839
|
+
#
|
|
840
|
+
# r = Ractor.new {raise "Something weird happened"}
|
|
575
841
|
#
|
|
576
|
-
|
|
842
|
+
# begin
|
|
843
|
+
# r.take
|
|
844
|
+
# rescue => e
|
|
845
|
+
# p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
|
|
846
|
+
# p e.ractor == r # => true
|
|
847
|
+
# p e.cause # => #<RuntimeError: Something weird happened>
|
|
848
|
+
# end
|
|
849
|
+
#
|
|
850
|
+
# Ractor::ClosedError is a descendant of StopIteration, so the termination of
|
|
851
|
+
# the ractor will break out of any loops that receive this message without
|
|
852
|
+
# propagating the error:
|
|
853
|
+
#
|
|
854
|
+
# r = Ractor.new do
|
|
855
|
+
# 3.times {|i| Ractor.yield "message #{i}"}
|
|
856
|
+
# "finishing"
|
|
857
|
+
# end
|
|
858
|
+
#
|
|
859
|
+
# loop {puts "Received: " + r.take}
|
|
860
|
+
# puts "Continue successfully"
|
|
861
|
+
#
|
|
862
|
+
# This will print:
|
|
863
|
+
#
|
|
864
|
+
# Received: message 0
|
|
865
|
+
# Received: message 1
|
|
866
|
+
# Received: message 2
|
|
867
|
+
# Received: finishing
|
|
868
|
+
# Continue successfully
|
|
869
|
+
#
|
|
870
|
+
def take: () -> untyped
|
|
577
871
|
|
|
578
872
|
# <!--
|
|
579
873
|
# rdoc-file=ractor.rb
|
|
580
|
-
# -
|
|
874
|
+
# - to_s()
|
|
581
875
|
# -->
|
|
582
|
-
# Waits for `ractor` to complete and returns its value or raises the exception
|
|
583
|
-
# which terminated the Ractor. The termination value will be moved to the
|
|
584
|
-
# calling Ractor. Therefore, at most 1 Ractor can receive another ractor's
|
|
585
|
-
# termination value.
|
|
586
|
-
#
|
|
587
|
-
# r = Ractor.new{ [1, 2] }
|
|
588
|
-
# r.value #=> [1, 2] (unshareable object)
|
|
589
|
-
#
|
|
590
|
-
# Ractor.new(r){|r| r.value} #=> Ractor::Error
|
|
591
876
|
#
|
|
592
|
-
|
|
877
|
+
alias to_s inspect
|
|
593
878
|
|
|
594
879
|
private
|
|
595
880
|
|
|
@@ -619,35 +904,49 @@ class Ractor
|
|
|
619
904
|
# <!-- rdoc-file=ractor.c -->
|
|
620
905
|
# Raised when an attempt is made to send a message to a closed port, or to
|
|
621
906
|
# retrieve a message from a closed and empty port. Ports may be closed
|
|
622
|
-
# explicitly with Ractor
|
|
623
|
-
# terminates.
|
|
907
|
+
# explicitly with Ractor#close_outgoing/close_incoming and are closed implicitly
|
|
908
|
+
# when a Ractor terminates.
|
|
909
|
+
#
|
|
910
|
+
# r = Ractor.new { sleep(500) }
|
|
911
|
+
# r.close_outgoing
|
|
912
|
+
# r.take # Ractor::ClosedError
|
|
913
|
+
#
|
|
914
|
+
# ClosedError is a descendant of StopIteration, so the closing of the ractor
|
|
915
|
+
# will break the loops without propagating the error:
|
|
916
|
+
#
|
|
917
|
+
# r = Ractor.new do
|
|
918
|
+
# loop do
|
|
919
|
+
# msg = receive # raises ClosedError and loop traps it
|
|
920
|
+
# puts "Received: #{msg}"
|
|
921
|
+
# end
|
|
922
|
+
# puts "loop exited"
|
|
923
|
+
# end
|
|
624
924
|
#
|
|
625
|
-
#
|
|
626
|
-
#
|
|
627
|
-
#
|
|
628
|
-
#
|
|
925
|
+
# 3.times{|i| r << i}
|
|
926
|
+
# r.close_incoming
|
|
927
|
+
# r.take
|
|
928
|
+
# puts "Continue successfully"
|
|
629
929
|
#
|
|
630
|
-
#
|
|
631
|
-
#
|
|
930
|
+
# This will print:
|
|
931
|
+
#
|
|
932
|
+
# Received: 0
|
|
933
|
+
# Received: 1
|
|
934
|
+
# Received: 2
|
|
935
|
+
# loop exited
|
|
936
|
+
# Continue successfully
|
|
632
937
|
#
|
|
633
938
|
class ClosedError < StopIteration
|
|
634
939
|
end
|
|
635
940
|
|
|
636
|
-
# <!-- rdoc-file=ractor.c -->
|
|
637
|
-
# The parent class of Ractor-related error classes.
|
|
638
|
-
#
|
|
639
941
|
class Error < RuntimeError
|
|
640
942
|
end
|
|
641
943
|
|
|
642
|
-
# <!-- rdoc-file=ractor.c -->
|
|
643
|
-
# Raised on attempt to make a Ractor-unshareable object Ractor-shareable.
|
|
644
|
-
#
|
|
645
944
|
class IsolationError < Ractor::Error
|
|
646
945
|
end
|
|
647
946
|
|
|
648
947
|
# <!-- rdoc-file=ractor.c -->
|
|
649
948
|
# Raised on an attempt to access an object which was moved in Ractor#send or
|
|
650
|
-
# Ractor
|
|
949
|
+
# Ractor.yield.
|
|
651
950
|
#
|
|
652
951
|
# r = Ractor.new { sleep }
|
|
653
952
|
#
|
|
@@ -661,7 +960,7 @@ class Ractor
|
|
|
661
960
|
|
|
662
961
|
# <!-- rdoc-file=ractor.c -->
|
|
663
962
|
# A special object which replaces any value that was moved to another ractor in
|
|
664
|
-
# Ractor#send or Ractor
|
|
963
|
+
# Ractor#send or Ractor.yield. Any attempt to access the object results in
|
|
665
964
|
# Ractor::MovedError.
|
|
666
965
|
#
|
|
667
966
|
# r = Ractor.new { receive }
|
|
@@ -738,163 +1037,15 @@ class Ractor
|
|
|
738
1037
|
def method_missing: (*untyped) -> untyped
|
|
739
1038
|
end
|
|
740
1039
|
|
|
741
|
-
# <!-- rdoc-file=ractor.rb -->
|
|
742
|
-
# Port objects transmit messages between Ractors.
|
|
743
|
-
#
|
|
744
|
-
class Port[T = untyped]
|
|
745
|
-
# <!--
|
|
746
|
-
# rdoc-file=ractor.rb
|
|
747
|
-
# - <<(obj, move: false)
|
|
748
|
-
# -->
|
|
749
|
-
#
|
|
750
|
-
alias << send
|
|
751
|
-
|
|
752
|
-
# <!--
|
|
753
|
-
# rdoc-file=ractor.rb
|
|
754
|
-
# - port.close
|
|
755
|
-
# -->
|
|
756
|
-
# Closes the port. Sending to a closed port is prohibited. Receiving is also
|
|
757
|
-
# prohibited if there are no messages in its message queue.
|
|
758
|
-
#
|
|
759
|
-
# Only the Ractor which created the port is allowed to close it.
|
|
760
|
-
#
|
|
761
|
-
# port = Ractor::Port.new
|
|
762
|
-
# Ractor.new port do |port|
|
|
763
|
-
# port.close #=> closing port by other ractors is not allowed (Ractor::Error)
|
|
764
|
-
# end.join
|
|
765
|
-
#
|
|
766
|
-
def close: () -> void
|
|
767
|
-
|
|
768
|
-
# <!--
|
|
769
|
-
# rdoc-file=ractor.rb
|
|
770
|
-
# - port.closed? -> true/false
|
|
771
|
-
# -->
|
|
772
|
-
# Returns whether or not the port is closed.
|
|
773
|
-
#
|
|
774
|
-
def closed?: () -> bool
|
|
775
|
-
|
|
776
|
-
# <!--
|
|
777
|
-
# rdoc-file=ractor.rb
|
|
778
|
-
# - port.inspect -> string
|
|
779
|
-
# -->
|
|
780
|
-
#
|
|
781
|
-
def inspect: () -> String
|
|
782
|
-
|
|
783
|
-
# <!--
|
|
784
|
-
# rdoc-file=ractor.rb
|
|
785
|
-
# - port.receive -> msg
|
|
786
|
-
# -->
|
|
787
|
-
# Receives a message from the port (which was sent there by Port#send). Only the
|
|
788
|
-
# ractor that created the port can receive messages this way.
|
|
789
|
-
#
|
|
790
|
-
# port = Ractor::Port.new
|
|
791
|
-
# r = Ractor.new port do |port|
|
|
792
|
-
# port.send('message1')
|
|
793
|
-
# end
|
|
794
|
-
#
|
|
795
|
-
# v1 = port.receive
|
|
796
|
-
# puts "Received: #{v1}"
|
|
797
|
-
# r.join
|
|
798
|
-
# # This will print: "Received: message1"
|
|
799
|
-
#
|
|
800
|
-
# The method blocks the current Thread if the message queue is empty.
|
|
801
|
-
#
|
|
802
|
-
# port = Ractor::Port.new
|
|
803
|
-
# r = Ractor.new port do |port|
|
|
804
|
-
# wait
|
|
805
|
-
# puts "Still not received"
|
|
806
|
-
# port.send('message1')
|
|
807
|
-
# wait
|
|
808
|
-
# puts "Still received only one"
|
|
809
|
-
# port.send('message2')
|
|
810
|
-
# end
|
|
811
|
-
# puts "Before first receive"
|
|
812
|
-
# v1 = port.receive
|
|
813
|
-
# puts "Received: #{v1}"
|
|
814
|
-
# v2 = port.receive
|
|
815
|
-
# puts "Received: #{v2}"
|
|
816
|
-
# r.join
|
|
817
|
-
#
|
|
818
|
-
# Output:
|
|
819
|
-
#
|
|
820
|
-
# Before first receive
|
|
821
|
-
# Still not received
|
|
822
|
-
# Received: message1
|
|
823
|
-
# Still received only one
|
|
824
|
-
# Received: message2
|
|
825
|
-
#
|
|
826
|
-
# If the port is closed and there are no more messages in the message queue, the
|
|
827
|
-
# method raises Ractor::ClosedError.
|
|
828
|
-
#
|
|
829
|
-
# port = Ractor::Port.new
|
|
830
|
-
# port.close
|
|
831
|
-
# port.receive #=> raise Ractor::ClosedError
|
|
832
|
-
#
|
|
833
|
-
def receive: () -> T
|
|
834
|
-
|
|
835
|
-
# <!--
|
|
836
|
-
# rdoc-file=ractor.rb
|
|
837
|
-
# - port.send(msg, move: false) -> self
|
|
838
|
-
# -->
|
|
839
|
-
# Sends a message to the port to be accepted by port.receive.
|
|
840
|
-
#
|
|
841
|
-
# port = Ractor::Port.new
|
|
842
|
-
# r = Ractor.new(port) do |port|
|
|
843
|
-
# port.send 'message'
|
|
844
|
-
# end
|
|
845
|
-
# value = port.receive
|
|
846
|
-
# puts "Received #{value}"
|
|
847
|
-
# # Prints: "Received: message"
|
|
848
|
-
#
|
|
849
|
-
# The method is non-blocking (it will return immediately even if the ractor that
|
|
850
|
-
# created the port is not ready to receive anything):
|
|
851
|
-
#
|
|
852
|
-
# port = Ractor::Port.new
|
|
853
|
-
# r = Ractor.new(port) do |port|
|
|
854
|
-
# port.send 'test'
|
|
855
|
-
# puts "Sent successfully"
|
|
856
|
-
# # Prints: "Sent successfully" immediately
|
|
857
|
-
# end
|
|
858
|
-
#
|
|
859
|
-
# An attempt to send to a closed port will raise Ractor::ClosedError.
|
|
860
|
-
#
|
|
861
|
-
# r = Ractor.new {Ractor::Port.new}
|
|
862
|
-
# r.join
|
|
863
|
-
# p r
|
|
864
|
-
# # "#<Ractor:#6 (irb):23 terminated>"
|
|
865
|
-
# port = r.value
|
|
866
|
-
# port.send('test') # raise Ractor::ClosedError
|
|
867
|
-
#
|
|
868
|
-
# If the `obj` is unshareable, by default it will be copied into the receiving
|
|
869
|
-
# ractor by deep cloning.
|
|
870
|
-
#
|
|
871
|
-
# If the object is shareable, a reference to the object will be sent to the
|
|
872
|
-
# receiving ractor.
|
|
873
|
-
#
|
|
874
|
-
def send: (T obj, ?move: boolish) -> self
|
|
875
|
-
|
|
876
|
-
private
|
|
877
|
-
|
|
878
|
-
# <!--
|
|
879
|
-
# rdoc-file=ractor_sync.c
|
|
880
|
-
# - Ractor::Port.new -> new_port
|
|
881
|
-
# -->
|
|
882
|
-
# Returns a new Ractor::Port object.
|
|
883
|
-
#
|
|
884
|
-
def initialize: () -> void
|
|
885
|
-
|
|
886
|
-
def initialize_copy: (untyped) -> untyped
|
|
887
|
-
end
|
|
888
|
-
|
|
889
1040
|
# <!-- rdoc-file=ractor.c -->
|
|
890
|
-
# Raised on
|
|
891
|
-
#
|
|
892
|
-
#
|
|
1041
|
+
# Raised on attempt to Ractor#take if there was an uncaught exception in the
|
|
1042
|
+
# Ractor. Its `cause` will contain the original exception, and `ractor` is the
|
|
1043
|
+
# original ractor it was raised in.
|
|
893
1044
|
#
|
|
894
1045
|
# r = Ractor.new { raise "Something weird happened" }
|
|
895
1046
|
#
|
|
896
1047
|
# begin
|
|
897
|
-
# r.
|
|
1048
|
+
# r.take
|
|
898
1049
|
# rescue => e
|
|
899
1050
|
# p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
|
|
900
1051
|
# p e.ractor == r # => true
|
|
@@ -903,14 +1054,11 @@ class Ractor
|
|
|
903
1054
|
#
|
|
904
1055
|
class RemoteError < Ractor::Error
|
|
905
1056
|
# <!-- rdoc-file=ractor.rb -->
|
|
906
|
-
# The Ractor
|
|
1057
|
+
# The Ractor an uncaught exception is raised in.
|
|
907
1058
|
#
|
|
908
1059
|
def ractor: () -> Ractor
|
|
909
1060
|
end
|
|
910
1061
|
|
|
911
|
-
# <!-- rdoc-file=ractor.c -->
|
|
912
|
-
# Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
|
|
913
|
-
#
|
|
914
1062
|
class UnsafeError < Ractor::Error
|
|
915
1063
|
end
|
|
916
1064
|
|