rbs 4.0.0.dev.4 → 4.0.0.dev.5
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/dependabot.yml +14 -14
- data/.github/workflows/bundle-update.yml +60 -0
- data/.github/workflows/c-check.yml +11 -8
- data/.github/workflows/comments.yml +3 -3
- data/.github/workflows/dependabot.yml +1 -1
- data/.github/workflows/ruby.yml +17 -34
- data/.github/workflows/typecheck.yml +2 -2
- data/.github/workflows/valgrind.yml +42 -0
- data/.github/workflows/windows.yml +2 -2
- data/.rubocop.yml +1 -1
- data/README.md +1 -1
- data/Rakefile +32 -5
- data/config.yml +46 -0
- data/core/array.rbs +96 -46
- data/core/binding.rbs +0 -2
- data/core/builtin.rbs +2 -2
- data/core/comparable.rbs +13 -6
- data/core/complex.rbs +55 -41
- data/core/dir.rbs +4 -4
- data/core/encoding.rbs +7 -10
- data/core/enumerable.rbs +90 -3
- data/core/enumerator/arithmetic_sequence.rbs +70 -0
- data/core/enumerator.rbs +63 -1
- data/core/errno.rbs +8 -0
- data/core/errors.rbs +28 -1
- data/core/exception.rbs +2 -2
- data/core/fiber.rbs +40 -20
- data/core/file.rbs +108 -78
- data/core/file_test.rbs +1 -1
- data/core/float.rbs +225 -69
- data/core/gc.rbs +417 -281
- data/core/hash.rbs +1023 -727
- data/core/integer.rbs +104 -110
- data/core/io/buffer.rbs +21 -10
- data/core/io/wait.rbs +11 -33
- data/core/io.rbs +82 -19
- data/core/kernel.rbs +70 -59
- data/core/marshal.rbs +1 -1
- data/core/match_data.rbs +1 -1
- data/core/math.rbs +42 -3
- data/core/method.rbs +63 -27
- data/core/module.rbs +103 -26
- data/core/nil_class.rbs +3 -3
- data/core/numeric.rbs +43 -35
- data/core/object.rbs +3 -3
- data/core/object_space.rbs +21 -15
- data/core/pathname.rbs +1272 -0
- data/core/proc.rbs +30 -25
- data/core/process.rbs +4 -2
- data/core/ractor.rbs +361 -509
- data/core/random.rbs +17 -0
- data/core/range.rbs +113 -16
- data/core/rational.rbs +56 -85
- data/core/rbs/unnamed/argf.rbs +2 -2
- data/core/rbs/unnamed/env_class.rbs +1 -1
- data/core/rbs/unnamed/random.rbs +4 -113
- data/core/regexp.rbs +25 -20
- data/core/ruby.rbs +53 -0
- data/core/ruby_vm.rbs +6 -4
- data/core/rubygems/errors.rbs +3 -70
- data/core/rubygems/rubygems.rbs +11 -79
- data/core/rubygems/version.rbs +2 -3
- data/core/set.rbs +488 -359
- data/core/signal.rbs +24 -14
- data/core/string.rbs +3171 -1241
- data/core/struct.rbs +1 -1
- data/core/symbol.rbs +17 -11
- data/core/thread.rbs +95 -33
- data/core/time.rbs +35 -9
- data/core/trace_point.rbs +7 -4
- data/core/unbound_method.rbs +14 -6
- data/docs/aliases.md +79 -0
- data/docs/collection.md +2 -2
- data/docs/encoding.md +56 -0
- data/docs/gem.md +0 -1
- data/docs/inline.md +470 -0
- data/docs/sigs.md +3 -3
- data/docs/syntax.md +33 -4
- data/docs/type_fingerprint.md +21 -0
- data/exe/rbs +1 -1
- data/ext/rbs_extension/ast_translation.c +77 -3
- data/ext/rbs_extension/ast_translation.h +3 -0
- data/ext/rbs_extension/class_constants.c +8 -2
- data/ext/rbs_extension/class_constants.h +4 -0
- data/ext/rbs_extension/extconf.rb +5 -1
- data/ext/rbs_extension/legacy_location.c +5 -5
- data/ext/rbs_extension/main.c +37 -20
- data/include/rbs/ast.h +85 -38
- data/include/rbs/defines.h +27 -0
- data/include/rbs/lexer.h +30 -11
- data/include/rbs/parser.h +6 -6
- data/include/rbs/string.h +0 -2
- data/include/rbs/util/rbs_allocator.h +34 -13
- data/include/rbs/util/rbs_assert.h +12 -1
- data/include/rbs/util/rbs_encoding.h +2 -0
- data/include/rbs/util/rbs_unescape.h +2 -1
- data/lib/rbs/ast/annotation.rb +1 -1
- data/lib/rbs/ast/comment.rb +1 -1
- data/lib/rbs/ast/declarations.rb +10 -10
- data/lib/rbs/ast/members.rb +14 -14
- data/lib/rbs/ast/ruby/annotations.rb +137 -0
- data/lib/rbs/ast/ruby/comment_block.rb +24 -0
- data/lib/rbs/ast/ruby/declarations.rb +198 -3
- data/lib/rbs/ast/ruby/helpers/constant_helper.rb +4 -0
- data/lib/rbs/ast/ruby/members.rb +159 -1
- data/lib/rbs/ast/type_param.rb +24 -4
- data/lib/rbs/buffer.rb +20 -15
- data/lib/rbs/cli/diff.rb +16 -15
- data/lib/rbs/cli/validate.rb +38 -51
- data/lib/rbs/cli.rb +52 -19
- data/lib/rbs/collection/config/lockfile_generator.rb +8 -0
- data/lib/rbs/collection/sources/git.rb +1 -0
- data/lib/rbs/definition.rb +1 -1
- data/lib/rbs/definition_builder/ancestor_builder.rb +62 -9
- data/lib/rbs/definition_builder/method_builder.rb +20 -0
- data/lib/rbs/definition_builder.rb +91 -2
- data/lib/rbs/diff.rb +7 -1
- data/lib/rbs/environment.rb +227 -74
- data/lib/rbs/environment_loader.rb +0 -6
- data/lib/rbs/errors.rb +27 -7
- data/lib/rbs/inline_parser.rb +341 -5
- data/lib/rbs/location_aux.rb +1 -1
- data/lib/rbs/locator.rb +5 -1
- data/lib/rbs/method_type.rb +5 -3
- data/lib/rbs/parser_aux.rb +2 -2
- data/lib/rbs/prototype/rb.rb +2 -2
- data/lib/rbs/prototype/rbi.rb +2 -0
- data/lib/rbs/prototype/runtime.rb +8 -0
- data/lib/rbs/resolver/constant_resolver.rb +2 -2
- data/lib/rbs/resolver/type_name_resolver.rb +116 -38
- data/lib/rbs/subtractor.rb +3 -1
- data/lib/rbs/test/type_check.rb +16 -2
- data/lib/rbs/type_name.rb +1 -1
- data/lib/rbs/types.rb +27 -27
- data/lib/rbs/validator.rb +2 -2
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs.rb +1 -1
- data/lib/rdoc/discover.rb +1 -1
- data/lib/rdoc_plugin/parser.rb +1 -1
- data/rbs.gemspec +3 -2
- data/schema/typeParam.json +17 -1
- data/sig/ast/ruby/annotations.rbs +124 -0
- data/sig/ast/ruby/comment_block.rbs +8 -0
- data/sig/ast/ruby/declarations.rbs +102 -4
- data/sig/ast/ruby/members.rbs +87 -1
- data/sig/cli/diff.rbs +5 -11
- data/sig/cli/validate.rbs +13 -4
- data/sig/cli.rbs +18 -18
- data/sig/definition.rbs +6 -1
- data/sig/environment.rbs +70 -12
- data/sig/errors.rbs +13 -6
- data/sig/inline_parser.rbs +39 -2
- data/sig/locator.rbs +0 -2
- data/sig/manifest.yaml +0 -1
- data/sig/method_builder.rbs +3 -1
- data/sig/method_types.rbs +1 -1
- data/sig/parser.rbs +16 -2
- data/sig/resolver/type_name_resolver.rbs +35 -7
- data/sig/source.rbs +3 -3
- data/sig/type_param.rbs +13 -8
- data/sig/types.rbs +4 -4
- data/src/ast.c +80 -1
- data/src/lexer.c +1392 -1313
- data/src/lexer.re +3 -0
- data/src/lexstate.c +58 -37
- data/src/location.c +4 -4
- data/src/parser.c +412 -145
- data/src/string.c +0 -48
- data/src/util/rbs_allocator.c +89 -71
- data/src/util/rbs_assert.c +1 -1
- data/src/util/rbs_buffer.c +2 -2
- data/src/util/rbs_constant_pool.c +10 -10
- data/src/util/rbs_encoding.c +4 -8
- data/src/util/rbs_unescape.c +56 -20
- data/stdlib/bigdecimal/0/big_decimal.rbs +100 -82
- data/stdlib/bigdecimal-math/0/big_math.rbs +169 -8
- data/stdlib/cgi/0/core.rbs +9 -393
- data/stdlib/cgi/0/manifest.yaml +1 -0
- data/stdlib/cgi-escape/0/escape.rbs +171 -0
- data/stdlib/coverage/0/coverage.rbs +3 -1
- data/stdlib/date/0/date.rbs +67 -59
- data/stdlib/date/0/date_time.rbs +1 -1
- data/stdlib/delegate/0/delegator.rbs +10 -7
- data/stdlib/digest/0/digest.rbs +110 -0
- data/stdlib/erb/0/erb.rbs +737 -347
- data/stdlib/fileutils/0/fileutils.rbs +20 -14
- data/stdlib/forwardable/0/forwardable.rbs +3 -0
- data/stdlib/json/0/json.rbs +82 -28
- data/stdlib/net-http/0/net-http.rbs +3 -0
- data/stdlib/objspace/0/objspace.rbs +9 -27
- data/stdlib/open-uri/0/open-uri.rbs +40 -0
- data/stdlib/open3/0/open3.rbs +459 -1
- data/stdlib/openssl/0/openssl.rbs +331 -228
- data/stdlib/optparse/0/optparse.rbs +8 -3
- data/stdlib/pathname/0/pathname.rbs +9 -1379
- data/stdlib/psych/0/psych.rbs +4 -4
- data/stdlib/random-formatter/0/random-formatter.rbs +277 -0
- data/stdlib/rdoc/0/code_object.rbs +2 -1
- data/stdlib/rdoc/0/parser.rbs +1 -1
- data/stdlib/rdoc/0/rdoc.rbs +1 -1
- data/stdlib/rdoc/0/store.rbs +1 -1
- data/stdlib/resolv/0/resolv.rbs +25 -68
- data/stdlib/ripper/0/ripper.rbs +2 -2
- data/stdlib/securerandom/0/manifest.yaml +2 -0
- data/stdlib/securerandom/0/securerandom.rbs +6 -19
- data/stdlib/singleton/0/singleton.rbs +3 -0
- data/stdlib/socket/0/socket.rbs +13 -1
- data/stdlib/socket/0/tcp_socket.rbs +10 -2
- data/stdlib/stringio/0/stringio.rbs +1176 -85
- 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 +63 -7
- data/stdlib/tsort/0/cyclic.rbs +3 -0
- data/stdlib/uri/0/common.rbs +16 -2
- data/stdlib/uri/0/file.rbs +1 -1
- data/stdlib/uri/0/generic.rbs +24 -16
- data/stdlib/uri/0/rfc2396_parser.rbs +6 -7
- data/stdlib/zlib/0/gzip_reader.rbs +2 -2
- data/stdlib/zlib/0/gzip_writer.rbs +1 -1
- data/stdlib/zlib/0/zstream.rbs +1 -0
- metadata +30 -4
data/core/ractor.rbs
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
# <!-- rdoc-file=ractor.rb -->
|
|
2
|
-
# Ractor
|
|
3
|
-
# parallel execution.
|
|
4
|
-
#
|
|
5
|
-
# Ractor.new makes a new Ractor, which can run in parallel.
|
|
2
|
+
# Ractor.new creates a new Ractor, which can run in parallel with other ractors.
|
|
6
3
|
#
|
|
7
4
|
# # The simplest ractor
|
|
8
5
|
# r = Ractor.new {puts "I am in Ractor!"}
|
|
9
|
-
# r.
|
|
6
|
+
# r.join # wait for it to finish
|
|
10
7
|
# # Here, "I am in Ractor!" is printed
|
|
11
8
|
#
|
|
12
9
|
# Ractors do not share all objects with each other. There are two main benefits
|
|
13
10
|
# to this: across ractors, thread-safety concerns such as data-races and
|
|
14
11
|
# race-conditions are not possible. The other benefit is parallelism.
|
|
15
12
|
#
|
|
16
|
-
# To achieve this, object sharing is limited across ractors.
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
13
|
+
# To achieve this, object sharing is limited across ractors. Unlike in threads,
|
|
14
|
+
# ractors can't access all the objects available in other ractors. For example,
|
|
15
|
+
# objects normally available through variables in the outer scope are prohibited
|
|
16
|
+
# from being used across ractors.
|
|
20
17
|
#
|
|
21
18
|
# a = 1
|
|
22
19
|
# r = Ractor.new {puts "I am in Ractor! a=#{a}"}
|
|
@@ -27,80 +24,37 @@
|
|
|
27
24
|
# a = 1
|
|
28
25
|
# r = Ractor.new(a) { |a1| puts "I am in Ractor! a=#{a1}"}
|
|
29
26
|
#
|
|
30
|
-
# On CRuby (the default implementation), Global Virtual Machine Lock (GVL)
|
|
31
|
-
# held per ractor, so ractors can
|
|
32
|
-
#
|
|
27
|
+
# On CRuby (the default implementation), the Global Virtual Machine Lock (GVL)
|
|
28
|
+
# is held per ractor, so ractors can run in parallel. This is unlike the
|
|
29
|
+
# situation with threads on CRuby.
|
|
33
30
|
#
|
|
34
31
|
# Instead of accessing shared state, objects should be passed to and from
|
|
35
32
|
# ractors by sending and receiving them as messages.
|
|
36
33
|
#
|
|
37
34
|
# a = 1
|
|
38
35
|
# r = Ractor.new do
|
|
39
|
-
# a_in_ractor = receive # receive blocks until
|
|
36
|
+
# a_in_ractor = receive # receive blocks the Thread until our default port gets sent a message
|
|
40
37
|
# puts "I am in Ractor! a=#{a_in_ractor}"
|
|
41
38
|
# end
|
|
42
39
|
# r.send(a) # pass it
|
|
43
|
-
# r.
|
|
40
|
+
# r.join
|
|
44
41
|
# # Here, "I am in Ractor! a=1" is printed
|
|
45
42
|
#
|
|
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
|
-
#
|
|
53
43
|
# In addition to that, any arguments passed to Ractor.new are passed to the
|
|
54
44
|
# block and available there as if received by Ractor.receive, and the last block
|
|
55
|
-
# value
|
|
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.
|
|
45
|
+
# value can be received with Ractor#value.
|
|
92
46
|
#
|
|
93
47
|
# ## Shareable and unshareable objects
|
|
94
48
|
#
|
|
95
|
-
# When an object is sent to
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
49
|
+
# When an object is sent to a ractor, it's important to understand whether the
|
|
50
|
+
# object is shareable or unshareable. Most Ruby objects are unshareable objects.
|
|
51
|
+
# Even frozen objects can be unshareable if they contain (through their instance
|
|
52
|
+
# variables) unfrozen objects.
|
|
99
53
|
#
|
|
100
|
-
# Shareable objects are those which can be used by several
|
|
101
|
-
# compromising thread-safety, for example numbers, `true` and `false`.
|
|
54
|
+
# Shareable objects are those which can be used by several ractors at once
|
|
55
|
+
# without compromising thread-safety, for example numbers, `true` and `false`.
|
|
102
56
|
# Ractor.shareable? allows you to check this, and Ractor.make_shareable tries to
|
|
103
|
-
# make the object shareable if it's not already
|
|
57
|
+
# make the object shareable if it's not already and gives an error if it can't
|
|
104
58
|
# do it.
|
|
105
59
|
#
|
|
106
60
|
# Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are shareable
|
|
@@ -116,25 +70,25 @@
|
|
|
116
70
|
# ary[0].frozen? #=> true
|
|
117
71
|
# ary[1].frozen? #=> true
|
|
118
72
|
#
|
|
119
|
-
# When a shareable object is sent
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
# the
|
|
123
|
-
#
|
|
73
|
+
# When a shareable object is sent via #send, no additional processing occurs on
|
|
74
|
+
# it and it becomes usable by both ractors. When an unshareable object is sent,
|
|
75
|
+
# it can be either *copied* or *moved*. Copying is the default, and it copies
|
|
76
|
+
# the object fully by deep cloning (Object#clone) the non-shareable parts of its
|
|
77
|
+
# structure.
|
|
124
78
|
#
|
|
125
|
-
# data = ['foo', 'bar'.freeze]
|
|
79
|
+
# data = ['foo'.dup, 'bar'.freeze]
|
|
126
80
|
# r = Ractor.new do
|
|
127
81
|
# data2 = Ractor.receive
|
|
128
82
|
# puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}"
|
|
129
83
|
# end
|
|
130
84
|
# r.send(data)
|
|
131
|
-
# r.
|
|
85
|
+
# r.join
|
|
132
86
|
# puts "Outside : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
|
|
133
87
|
#
|
|
134
88
|
# This will output something like:
|
|
135
89
|
#
|
|
136
|
-
# In ractor:
|
|
137
|
-
# Outside :
|
|
90
|
+
# In ractor: 8, 16, 24
|
|
91
|
+
# Outside : 32, 40, 24
|
|
138
92
|
#
|
|
139
93
|
# Note that the object ids of the array and the non-frozen string inside the
|
|
140
94
|
# array have changed in the ractor because they are different objects. The
|
|
@@ -151,7 +105,7 @@
|
|
|
151
105
|
# puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}"
|
|
152
106
|
# end
|
|
153
107
|
# r.send(data, move: true)
|
|
154
|
-
# r.
|
|
108
|
+
# r.join
|
|
155
109
|
# puts "Outside: moved? #{Ractor::MovedObject === data}"
|
|
156
110
|
# puts "Outside: #{data.inspect}"
|
|
157
111
|
#
|
|
@@ -161,14 +115,14 @@
|
|
|
161
115
|
# Outside: moved? true
|
|
162
116
|
# test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
|
|
163
117
|
#
|
|
164
|
-
# Notice that even `inspect`
|
|
118
|
+
# Notice that even `inspect` and more basic methods like `__id__` are
|
|
165
119
|
# inaccessible on a moved object.
|
|
166
120
|
#
|
|
167
|
-
# Class and Module objects are shareable
|
|
168
|
-
# shared between ractors. Ractor objects are also shareable. All operations
|
|
169
|
-
# shareable objects are thread-safe
|
|
170
|
-
#
|
|
171
|
-
#
|
|
121
|
+
# `Class` and `Module` objects are shareable and their class/module definitions
|
|
122
|
+
# are shared between ractors. Ractor objects are also shareable. All operations
|
|
123
|
+
# on shareable objects are thread-safe across ractors. Defining mutable,
|
|
124
|
+
# shareable objects in Ruby is not possible, but C extensions can introduce
|
|
125
|
+
# them.
|
|
172
126
|
#
|
|
173
127
|
# It is prohibited to access (get) instance variables of shareable objects in
|
|
174
128
|
# other ractors if the values of the variables aren't shareable. This can occur
|
|
@@ -189,7 +143,7 @@
|
|
|
189
143
|
# puts "I can't see #{cls.tricky}"
|
|
190
144
|
# cls.tricky = true # doesn't get here, but this would also raise an error
|
|
191
145
|
# end
|
|
192
|
-
# r.
|
|
146
|
+
# r.join
|
|
193
147
|
# # I see C
|
|
194
148
|
# # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
|
|
195
149
|
#
|
|
@@ -203,7 +157,7 @@
|
|
|
203
157
|
# puts "GOOD=#{GOOD}"
|
|
204
158
|
# puts "BAD=#{BAD}"
|
|
205
159
|
# end
|
|
206
|
-
# r.
|
|
160
|
+
# r.join
|
|
207
161
|
# # GOOD=good
|
|
208
162
|
# # can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError)
|
|
209
163
|
#
|
|
@@ -213,7 +167,7 @@
|
|
|
213
167
|
# puts "I see #{C}"
|
|
214
168
|
# puts "I can't see #{C.tricky}"
|
|
215
169
|
# end
|
|
216
|
-
# r.
|
|
170
|
+
# r.join
|
|
217
171
|
# # I see C
|
|
218
172
|
# # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
|
|
219
173
|
#
|
|
@@ -229,20 +183,21 @@
|
|
|
229
183
|
# a = 1
|
|
230
184
|
# Thread.new {puts "Thread in ractor: a=#{a}"}.join
|
|
231
185
|
# end
|
|
232
|
-
# r.
|
|
186
|
+
# r.join
|
|
233
187
|
# # Here "Thread in ractor: a=1" will be printed
|
|
234
188
|
#
|
|
235
189
|
# ## Note on code examples
|
|
236
190
|
#
|
|
237
191
|
# In the examples below, sometimes we use the following method to wait for
|
|
238
|
-
# ractors
|
|
192
|
+
# ractors to make progress or finish.
|
|
239
193
|
#
|
|
240
194
|
# def wait
|
|
241
195
|
# sleep(0.1)
|
|
242
196
|
# end
|
|
243
197
|
#
|
|
244
|
-
#
|
|
245
|
-
# code. Most of the time, #
|
|
198
|
+
# This is **only for demonstration purposes** and shouldn't be used in a real
|
|
199
|
+
# code. Most of the time, #join is used to wait for ractors to finish and
|
|
200
|
+
# Ractor.receive is used to wait for messages.
|
|
246
201
|
#
|
|
247
202
|
# ## Reference
|
|
248
203
|
#
|
|
@@ -261,7 +216,7 @@ class Ractor
|
|
|
261
216
|
# rdoc-file=ractor.rb
|
|
262
217
|
# - [](sym)
|
|
263
218
|
# -->
|
|
264
|
-
#
|
|
219
|
+
# Gets a value from ractor-local storage for the current Ractor.
|
|
265
220
|
#
|
|
266
221
|
def self.[]: (Symbol) -> untyped
|
|
267
222
|
|
|
@@ -269,7 +224,7 @@ class Ractor
|
|
|
269
224
|
# rdoc-file=ractor.rb
|
|
270
225
|
# - []=(sym, val)
|
|
271
226
|
# -->
|
|
272
|
-
#
|
|
227
|
+
# Sets a value in ractor-local storage for the current Ractor.
|
|
273
228
|
#
|
|
274
229
|
def self.[]=: (Symbol, untyped) -> untyped
|
|
275
230
|
|
|
@@ -277,13 +232,13 @@ class Ractor
|
|
|
277
232
|
# rdoc-file=ractor.rb
|
|
278
233
|
# - count()
|
|
279
234
|
# -->
|
|
280
|
-
# Returns the number of
|
|
235
|
+
# Returns the number of ractors currently running or blocking (waiting).
|
|
281
236
|
#
|
|
282
237
|
# Ractor.count #=> 1
|
|
283
|
-
# r = Ractor.new(name: 'example') { Ractor.
|
|
238
|
+
# r = Ractor.new(name: 'example') { Ractor.receive }
|
|
284
239
|
# Ractor.count #=> 2 (main + example ractor)
|
|
285
|
-
# r
|
|
286
|
-
# r.
|
|
240
|
+
# r << 42 # r's Ractor.receive will resume
|
|
241
|
+
# r.join # wait for r's termination
|
|
287
242
|
# Ractor.count #=> 1
|
|
288
243
|
#
|
|
289
244
|
def self.count: () -> Integer
|
|
@@ -302,7 +257,7 @@ class Ractor
|
|
|
302
257
|
# rdoc-file=ractor.rb
|
|
303
258
|
# - main()
|
|
304
259
|
# -->
|
|
305
|
-
#
|
|
260
|
+
# Returns the main ractor.
|
|
306
261
|
#
|
|
307
262
|
def self.main: () -> Ractor
|
|
308
263
|
|
|
@@ -310,7 +265,7 @@ class Ractor
|
|
|
310
265
|
# rdoc-file=ractor.rb
|
|
311
266
|
# - main?()
|
|
312
267
|
# -->
|
|
313
|
-
#
|
|
268
|
+
# Returns true if the current ractor is the main ractor.
|
|
314
269
|
#
|
|
315
270
|
def self.main?: () -> boolish
|
|
316
271
|
|
|
@@ -318,7 +273,7 @@ class Ractor
|
|
|
318
273
|
# rdoc-file=ractor.rb
|
|
319
274
|
# - Ractor.make_shareable(obj, copy: false) -> shareable_obj
|
|
320
275
|
# -->
|
|
321
|
-
#
|
|
276
|
+
# Makes `obj` shareable between ractors.
|
|
322
277
|
#
|
|
323
278
|
# `obj` and all the objects it refers to will be frozen, unless they are already
|
|
324
279
|
# shareable.
|
|
@@ -357,13 +312,13 @@ class Ractor
|
|
|
357
312
|
# rdoc-file=ractor.rb
|
|
358
313
|
# - Ractor.new(*args, name: nil) {|*args| block } -> ractor
|
|
359
314
|
# -->
|
|
360
|
-
#
|
|
315
|
+
# Creates a new Ractor with args and a block.
|
|
361
316
|
#
|
|
362
|
-
# The given block (Proc)
|
|
363
|
-
#
|
|
317
|
+
# The given block (Proc) is isolated (can't access any outer variables). `self`
|
|
318
|
+
# inside the block will refer to the current Ractor.
|
|
364
319
|
#
|
|
365
320
|
# r = Ractor.new { puts "Hi, I am #{self.inspect}" }
|
|
366
|
-
# r.
|
|
321
|
+
# r.join
|
|
367
322
|
# # Prints "Hi, I am #<Ractor:#2 test.rb:1 running>"
|
|
368
323
|
#
|
|
369
324
|
# Any `args` passed are propagated to the block arguments by the same rules as
|
|
@@ -375,14 +330,14 @@ class Ractor
|
|
|
375
330
|
# r = Ractor.new(arg) {|received_arg|
|
|
376
331
|
# puts "Received: #{received_arg} (##{received_arg.object_id})"
|
|
377
332
|
# }
|
|
378
|
-
# r.
|
|
333
|
+
# r.join
|
|
379
334
|
# # Prints:
|
|
380
335
|
# # Passing: [1, 2, 3] (#280)
|
|
381
336
|
# # Received: [1, 2, 3] (#300)
|
|
382
337
|
#
|
|
383
338
|
# Ractor's `name` can be set for debugging purposes:
|
|
384
339
|
#
|
|
385
|
-
# r = Ractor.new(name: 'my ractor') {}; r.
|
|
340
|
+
# r = Ractor.new(name: 'my ractor') {}; r.join
|
|
386
341
|
# p r
|
|
387
342
|
# #=> #<Ractor:#3 my ractor test.rb:1 terminated>
|
|
388
343
|
#
|
|
@@ -390,128 +345,12 @@ class Ractor
|
|
|
390
345
|
|
|
391
346
|
# <!--
|
|
392
347
|
# rdoc-file=ractor.rb
|
|
393
|
-
# - Ractor.receive ->
|
|
348
|
+
# - Ractor.receive -> obj
|
|
394
349
|
# -->
|
|
395
|
-
#
|
|
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)
|
|
350
|
+
# Receives a message from the current ractor's default port.
|
|
450
351
|
#
|
|
451
352
|
def self.receive: () -> untyped
|
|
452
353
|
|
|
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
|
-
|
|
515
354
|
# <!--
|
|
516
355
|
# rdoc-file=ractor.rb
|
|
517
356
|
# - recv()
|
|
@@ -521,57 +360,50 @@ class Ractor
|
|
|
521
360
|
|
|
522
361
|
# <!--
|
|
523
362
|
# rdoc-file=ractor.rb
|
|
524
|
-
# - Ractor.select(*
|
|
363
|
+
# - Ractor.select(*ractors_or_ports) -> [ractor or port, obj]
|
|
525
364
|
# -->
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
#
|
|
529
|
-
#
|
|
530
|
-
#
|
|
531
|
-
#
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
#
|
|
536
|
-
#
|
|
537
|
-
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
540
|
-
#
|
|
541
|
-
#
|
|
542
|
-
# main.send 'to main'
|
|
543
|
-
# Ractor.yield 'from 1'
|
|
544
|
-
# end
|
|
545
|
-
# r2 = Ractor.new do
|
|
546
|
-
# Ractor.yield 'from 2'
|
|
365
|
+
# Blocks the current Thread until one of the given ports has received a message.
|
|
366
|
+
# Returns an array of two elements where the first element is the Port and the
|
|
367
|
+
# second is the received object. This method can also accept Ractor objects
|
|
368
|
+
# themselves, and in that case will wait until one has terminated and return a
|
|
369
|
+
# two-element array where the first element is the ractor and the second is its
|
|
370
|
+
# termination value.
|
|
371
|
+
#
|
|
372
|
+
# p1, p2 = Ractor::Port.new, Ractor::Port.new
|
|
373
|
+
# ps = [p1, p2]
|
|
374
|
+
# rs = 2.times.map do |i|
|
|
375
|
+
# Ractor.new(ps.shift, i) do |p, i|
|
|
376
|
+
# sleep rand(0.99)
|
|
377
|
+
# p.send("r#{i}")
|
|
378
|
+
# sleep rand(0.99)
|
|
379
|
+
# "r#{i} done"
|
|
380
|
+
# end
|
|
547
381
|
# end
|
|
548
382
|
#
|
|
549
|
-
#
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
-
#
|
|
553
|
-
#
|
|
554
|
-
# calling #take. In this case, the pair `[:yield, nil]` is returned:
|
|
555
|
-
#
|
|
556
|
-
# r1 = Ractor.new(Ractor.current) do |main|
|
|
557
|
-
# puts "Received from main: #{main.take}"
|
|
383
|
+
# waiting_on = [p1, p2, *rs]
|
|
384
|
+
# until waiting_on.empty?
|
|
385
|
+
# received_on, obj = Ractor.select(*waiting_on)
|
|
386
|
+
# waiting_on.delete(received_on)
|
|
387
|
+
# puts obj
|
|
558
388
|
# end
|
|
559
389
|
#
|
|
560
|
-
#
|
|
561
|
-
#
|
|
562
|
-
#
|
|
563
|
-
#
|
|
564
|
-
#
|
|
565
|
-
# This will print:
|
|
390
|
+
# # r0
|
|
391
|
+
# # r1
|
|
392
|
+
# # r1 done
|
|
393
|
+
# # r0 done
|
|
566
394
|
#
|
|
567
|
-
#
|
|
568
|
-
#
|
|
569
|
-
#
|
|
395
|
+
# The following example is almost equivalent to `ractors.map(&:value)` except
|
|
396
|
+
# the thread is unblocked when any of the ractors has terminated as opposed to
|
|
397
|
+
# waiting for their termination in the array element order.
|
|
570
398
|
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
399
|
+
# values = []
|
|
400
|
+
# until ractors.empty?
|
|
401
|
+
# r, val = Ractor.select(*ractors)
|
|
402
|
+
# ractors.delete(r)
|
|
403
|
+
# values << val
|
|
404
|
+
# end
|
|
573
405
|
#
|
|
574
|
-
def self.select: (
|
|
406
|
+
def self.select: (?) -> Array[untyped]
|
|
575
407
|
|
|
576
408
|
# <!--
|
|
577
409
|
# rdoc-file=ractor.rb
|
|
@@ -579,7 +411,7 @@ class Ractor
|
|
|
579
411
|
# -->
|
|
580
412
|
# Checks if the object is shareable by ractors.
|
|
581
413
|
#
|
|
582
|
-
# Ractor.shareable?(1) #=> true -- numbers
|
|
414
|
+
# Ractor.shareable?(1) #=> true -- numbers are shareable
|
|
583
415
|
# Ractor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true
|
|
584
416
|
# Ractor.shareable?('foo'.freeze) #=> true
|
|
585
417
|
#
|
|
@@ -588,13 +420,44 @@ class Ractor
|
|
|
588
420
|
#
|
|
589
421
|
def self.shareable?: (untyped obj) -> bool
|
|
590
422
|
|
|
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
|
+
|
|
591
454
|
# <!--
|
|
592
455
|
# rdoc-file=ractor.rb
|
|
593
456
|
# - Ractor.store_if_absent(key){ init_block }
|
|
594
457
|
# -->
|
|
595
|
-
# If the
|
|
596
|
-
# the value in thread-safe manner. This method returns
|
|
597
|
-
# value.
|
|
458
|
+
# If the corresponding ractor-local value is not set, yields a value with
|
|
459
|
+
# init_block and stores the value in a thread-safe manner. This method returns
|
|
460
|
+
# the stored value.
|
|
598
461
|
#
|
|
599
462
|
# (1..10).map{
|
|
600
463
|
# Thread.new(it){|i|
|
|
@@ -607,47 +470,7 @@ class Ractor
|
|
|
607
470
|
|
|
608
471
|
# <!--
|
|
609
472
|
# rdoc-file=ractor.rb
|
|
610
|
-
# -
|
|
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)
|
|
473
|
+
# - <<(...)
|
|
651
474
|
# -->
|
|
652
475
|
#
|
|
653
476
|
alias << send
|
|
@@ -656,225 +479,117 @@ class Ractor
|
|
|
656
479
|
# rdoc-file=ractor.rb
|
|
657
480
|
# - [](sym)
|
|
658
481
|
# -->
|
|
659
|
-
#
|
|
482
|
+
# Gets a value from ractor-local storage for the current Ractor. Obsolete, use
|
|
660
483
|
# Ractor.[] instead.
|
|
661
484
|
#
|
|
485
|
+
%a{deprecated: Use Ractor.[] instead}
|
|
662
486
|
def []: (interned sym) -> untyped
|
|
663
487
|
|
|
664
488
|
# <!--
|
|
665
489
|
# rdoc-file=ractor.rb
|
|
666
490
|
# - []=(sym, val)
|
|
667
491
|
# -->
|
|
668
|
-
#
|
|
492
|
+
# Sets a value in ractor-local storage for the current Ractor. Obsolete, use
|
|
669
493
|
# Ractor.[]= instead.
|
|
670
494
|
#
|
|
495
|
+
%a{deprecated: Use Ractor.[]= instead}
|
|
671
496
|
def []=: [T] (interned sym, T val) -> T
|
|
672
497
|
|
|
673
498
|
# <!--
|
|
674
499
|
# rdoc-file=ractor.rb
|
|
675
|
-
# - ractor.
|
|
500
|
+
# - ractor.default_port -> port object
|
|
676
501
|
# -->
|
|
677
|
-
#
|
|
678
|
-
# further attempts to Ractor.receive in the ractor, and #send to the ractor will
|
|
679
|
-
# fail with Ractor::ClosedError.
|
|
502
|
+
# Returns the default port of the Ractor.
|
|
680
503
|
#
|
|
681
|
-
|
|
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
|
|
504
|
+
def default_port: () -> Port[untyped]
|
|
688
505
|
|
|
689
506
|
# <!--
|
|
690
507
|
# rdoc-file=ractor.rb
|
|
691
|
-
# -
|
|
508
|
+
# - inspect()
|
|
692
509
|
# -->
|
|
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.
|
|
696
|
-
#
|
|
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
510
|
#
|
|
703
|
-
def
|
|
511
|
+
def inspect: () -> String
|
|
704
512
|
|
|
705
513
|
# <!--
|
|
706
514
|
# rdoc-file=ractor.rb
|
|
707
|
-
# -
|
|
515
|
+
# - ractor.join -> self
|
|
708
516
|
# -->
|
|
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.
|
|
709
519
|
#
|
|
710
|
-
|
|
520
|
+
# Ractor.new{}.join #=> ractor
|
|
521
|
+
#
|
|
522
|
+
# Ractor.new{ raise "foo" }.join
|
|
523
|
+
# #=> raises the exception "foo (RuntimeError)"
|
|
524
|
+
#
|
|
525
|
+
def join: () -> self
|
|
711
526
|
|
|
712
527
|
# <!--
|
|
713
528
|
# rdoc-file=ractor.rb
|
|
714
529
|
# - name()
|
|
715
530
|
# -->
|
|
716
|
-
#
|
|
531
|
+
# Returns the name set in Ractor.new, or `nil`.
|
|
717
532
|
#
|
|
718
533
|
def name: () -> String?
|
|
719
534
|
|
|
720
535
|
# <!--
|
|
721
536
|
# rdoc-file=ractor.rb
|
|
722
|
-
# - ractor.
|
|
537
|
+
# - ractor.monitor(port) -> self
|
|
723
538
|
# -->
|
|
724
|
-
#
|
|
725
|
-
#
|
|
726
|
-
# r = Ractor.new do
|
|
727
|
-
# value = Ractor.receive
|
|
728
|
-
# puts "Received #{value}"
|
|
729
|
-
# end
|
|
730
|
-
# r.send 'message'
|
|
731
|
-
# # Prints: "Received: message"
|
|
732
|
-
#
|
|
733
|
-
# The method is non-blocking (will return immediately even if the ractor is not
|
|
734
|
-
# ready to receive anything):
|
|
735
|
-
#
|
|
736
|
-
# r = Ractor.new {sleep(5)}
|
|
737
|
-
# r.send('test')
|
|
738
|
-
# puts "Sent successfully"
|
|
739
|
-
# # Prints: "Sent successfully" immediately
|
|
740
|
-
#
|
|
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
|
|
539
|
+
# Registers the port as a monitoring port for this ractor. When the ractor
|
|
540
|
+
# terminates, the port receives a Symbol object.
|
|
772
541
|
#
|
|
773
|
-
#
|
|
542
|
+
# * `:exited` is sent if the ractor terminates without an unhandled exception.
|
|
543
|
+
# * `:aborted` is sent if the ractor terminates by an unhandled exception.
|
|
774
544
|
#
|
|
775
|
-
#
|
|
776
|
-
#
|
|
545
|
+
# r = Ractor.new{ some_task() }
|
|
546
|
+
# r.monitor(port = Ractor::Port.new)
|
|
547
|
+
# port.receive #=> :exited and r is terminated
|
|
777
548
|
#
|
|
778
|
-
#
|
|
549
|
+
# r = Ractor.new{ raise "foo" }
|
|
550
|
+
# r.monitor(port = Ractor::Port.new)
|
|
551
|
+
# port.receive #=> :aborted and r is terminated by the RuntimeError "foo"
|
|
779
552
|
#
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
#
|
|
783
|
-
#
|
|
784
|
-
#
|
|
785
|
-
#
|
|
786
|
-
#
|
|
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
|
|
553
|
+
def monitor: [T < Symbol] (Port[T]) -> untyped
|
|
554
|
+
|
|
555
|
+
# <!--
|
|
556
|
+
# rdoc-file=ractor.rb
|
|
557
|
+
# - ractor.send(msg, move: false) -> self
|
|
558
|
+
# -->
|
|
559
|
+
# This is equivalent to Port#send to the ractor's #default_port.
|
|
802
560
|
#
|
|
803
561
|
def send: (untyped obj, ?move: boolish) -> Ractor
|
|
804
562
|
|
|
805
563
|
# <!--
|
|
806
564
|
# rdoc-file=ractor.rb
|
|
807
|
-
# -
|
|
565
|
+
# - to_s()
|
|
808
566
|
# -->
|
|
809
|
-
# Get a message from the ractor's outgoing port, which was put there by
|
|
810
|
-
# Ractor.yield or at ractor's termination.
|
|
811
|
-
#
|
|
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"}
|
|
841
|
-
#
|
|
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
567
|
#
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
#
|
|
853
|
-
#
|
|
854
|
-
#
|
|
855
|
-
#
|
|
856
|
-
#
|
|
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
|
|
568
|
+
alias to_s inspect
|
|
569
|
+
|
|
570
|
+
# <!--
|
|
571
|
+
# rdoc-file=ractor.rb
|
|
572
|
+
# - ractor.unmonitor(port) -> self
|
|
573
|
+
# -->
|
|
574
|
+
# Unregisters the port from the monitoring ports for this ractor.
|
|
869
575
|
#
|
|
870
|
-
def
|
|
576
|
+
def unmonitor: (Port[untyped]) -> self
|
|
871
577
|
|
|
872
578
|
# <!--
|
|
873
579
|
# rdoc-file=ractor.rb
|
|
874
|
-
# -
|
|
580
|
+
# - ractor.value -> obj
|
|
875
581
|
# -->
|
|
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.
|
|
876
586
|
#
|
|
877
|
-
|
|
587
|
+
# r = Ractor.new{ [1, 2] }
|
|
588
|
+
# r.value #=> [1, 2] (unshareable object)
|
|
589
|
+
#
|
|
590
|
+
# Ractor.new(r){|r| r.value} #=> Ractor::Error
|
|
591
|
+
#
|
|
592
|
+
def value: () -> untyped
|
|
878
593
|
|
|
879
594
|
private
|
|
880
595
|
|
|
@@ -904,49 +619,35 @@ class Ractor
|
|
|
904
619
|
# <!-- rdoc-file=ractor.c -->
|
|
905
620
|
# Raised when an attempt is made to send a message to a closed port, or to
|
|
906
621
|
# retrieve a message from a closed and empty port. Ports may be closed
|
|
907
|
-
# explicitly with Ractor#
|
|
908
|
-
#
|
|
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
|
|
622
|
+
# explicitly with Ractor::Port#close and are closed implicitly when a Ractor
|
|
623
|
+
# terminates.
|
|
924
624
|
#
|
|
925
|
-
#
|
|
926
|
-
#
|
|
927
|
-
#
|
|
928
|
-
#
|
|
625
|
+
# port = Ractor::Port.new
|
|
626
|
+
# port.close
|
|
627
|
+
# port << "test" # Ractor::ClosedError
|
|
628
|
+
# port.receive # Ractor::ClosedError
|
|
929
629
|
#
|
|
930
|
-
#
|
|
931
|
-
#
|
|
932
|
-
# Received: 0
|
|
933
|
-
# Received: 1
|
|
934
|
-
# Received: 2
|
|
935
|
-
# loop exited
|
|
936
|
-
# Continue successfully
|
|
630
|
+
# ClosedError is a descendant of StopIteration, so the closing of a port will
|
|
631
|
+
# break out of loops without propagating the error.
|
|
937
632
|
#
|
|
938
633
|
class ClosedError < StopIteration
|
|
939
634
|
end
|
|
940
635
|
|
|
636
|
+
# <!-- rdoc-file=ractor.c -->
|
|
637
|
+
# The parent class of Ractor-related error classes.
|
|
638
|
+
#
|
|
941
639
|
class Error < RuntimeError
|
|
942
640
|
end
|
|
943
641
|
|
|
642
|
+
# <!-- rdoc-file=ractor.c -->
|
|
643
|
+
# Raised on attempt to make a Ractor-unshareable object Ractor-shareable.
|
|
644
|
+
#
|
|
944
645
|
class IsolationError < Ractor::Error
|
|
945
646
|
end
|
|
946
647
|
|
|
947
648
|
# <!-- rdoc-file=ractor.c -->
|
|
948
649
|
# Raised on an attempt to access an object which was moved in Ractor#send or
|
|
949
|
-
# Ractor.
|
|
650
|
+
# Ractor::Port#send.
|
|
950
651
|
#
|
|
951
652
|
# r = Ractor.new { sleep }
|
|
952
653
|
#
|
|
@@ -960,7 +661,7 @@ class Ractor
|
|
|
960
661
|
|
|
961
662
|
# <!-- rdoc-file=ractor.c -->
|
|
962
663
|
# A special object which replaces any value that was moved to another ractor in
|
|
963
|
-
# Ractor#send or Ractor.
|
|
664
|
+
# Ractor#send or Ractor::Port#send. Any attempt to access the object results in
|
|
964
665
|
# Ractor::MovedError.
|
|
965
666
|
#
|
|
966
667
|
# r = Ractor.new { receive }
|
|
@@ -1037,15 +738,163 @@ class Ractor
|
|
|
1037
738
|
def method_missing: (*untyped) -> untyped
|
|
1038
739
|
end
|
|
1039
740
|
|
|
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
|
+
|
|
1040
889
|
# <!-- rdoc-file=ractor.c -->
|
|
1041
|
-
# Raised on
|
|
1042
|
-
# Ractor. Its `cause` will contain the original exception, and `ractor` is
|
|
1043
|
-
# original ractor it was raised in.
|
|
890
|
+
# Raised on Ractor#join or Ractor#value if there was an uncaught exception in
|
|
891
|
+
# the Ractor. Its `cause` will contain the original exception, and `ractor` is
|
|
892
|
+
# the original ractor it was raised in.
|
|
1044
893
|
#
|
|
1045
894
|
# r = Ractor.new { raise "Something weird happened" }
|
|
1046
895
|
#
|
|
1047
896
|
# begin
|
|
1048
|
-
# r.
|
|
897
|
+
# r.value
|
|
1049
898
|
# rescue => e
|
|
1050
899
|
# p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
|
|
1051
900
|
# p e.ractor == r # => true
|
|
@@ -1054,11 +903,14 @@ class Ractor
|
|
|
1054
903
|
#
|
|
1055
904
|
class RemoteError < Ractor::Error
|
|
1056
905
|
# <!-- rdoc-file=ractor.rb -->
|
|
1057
|
-
# The Ractor
|
|
906
|
+
# The Ractor in which the uncaught exception was raised.
|
|
1058
907
|
#
|
|
1059
908
|
def ractor: () -> Ractor
|
|
1060
909
|
end
|
|
1061
910
|
|
|
911
|
+
# <!-- rdoc-file=ractor.c -->
|
|
912
|
+
# Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
|
|
913
|
+
#
|
|
1062
914
|
class UnsafeError < Ractor::Error
|
|
1063
915
|
end
|
|
1064
916
|
|