rbs 3.10.0 → 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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/comments.yml +3 -3
  3. data/.github/workflows/ruby.yml +24 -35
  4. data/.github/workflows/typecheck.yml +3 -1
  5. data/.github/workflows/windows.yml +2 -2
  6. data/.gitignore +0 -4
  7. data/CHANGELOG.md +0 -88
  8. data/README.md +1 -38
  9. data/Rakefile +20 -142
  10. data/Steepfile +1 -0
  11. data/config.yml +43 -1
  12. data/core/array.rbs +46 -100
  13. data/core/complex.rbs +21 -32
  14. data/core/dir.rbs +2 -2
  15. data/core/encoding.rbs +9 -6
  16. data/core/enumerable.rbs +3 -90
  17. data/core/enumerator.rbs +1 -18
  18. data/core/errno.rbs +0 -8
  19. data/core/errors.rbs +1 -28
  20. data/core/exception.rbs +2 -2
  21. data/core/fiber.rbs +4 -5
  22. data/core/file.rbs +12 -27
  23. data/core/file_test.rbs +1 -1
  24. data/core/float.rbs +22 -209
  25. data/core/gc.rbs +281 -417
  26. data/core/hash.rbs +727 -1024
  27. data/core/integer.rbs +38 -78
  28. data/core/io/buffer.rbs +7 -18
  29. data/core/io/wait.rbs +33 -11
  30. data/core/io.rbs +12 -14
  31. data/core/kernel.rbs +51 -57
  32. data/core/marshal.rbs +1 -1
  33. data/core/match_data.rbs +1 -1
  34. data/core/math.rbs +3 -42
  35. data/core/method.rbs +6 -14
  36. data/core/module.rbs +17 -88
  37. data/core/nil_class.rbs +3 -3
  38. data/core/numeric.rbs +16 -16
  39. data/core/object.rbs +3 -3
  40. data/core/object_space.rbs +15 -21
  41. data/core/proc.rbs +8 -15
  42. data/core/process.rbs +2 -2
  43. data/core/ractor.rbs +437 -278
  44. data/core/range.rbs +8 -7
  45. data/core/rational.rbs +24 -37
  46. data/core/rbs/unnamed/argf.rbs +2 -2
  47. data/core/rbs/unnamed/env_class.rbs +1 -1
  48. data/core/rbs/unnamed/random.rbs +2 -4
  49. data/core/regexp.rbs +20 -25
  50. data/core/ruby_vm.rbs +4 -6
  51. data/core/rubygems/errors.rbs +70 -3
  52. data/core/rubygems/rubygems.rbs +79 -11
  53. data/core/rubygems/version.rbs +3 -2
  54. data/core/set.rbs +359 -488
  55. data/core/string.rbs +1228 -3153
  56. data/core/struct.rbs +1 -1
  57. data/core/symbol.rbs +4 -4
  58. data/core/thread.rbs +29 -92
  59. data/core/time.rbs +9 -35
  60. data/core/trace_point.rbs +4 -7
  61. data/core/unbound_method.rbs +6 -14
  62. data/docs/collection.md +2 -2
  63. data/docs/gem.md +1 -0
  64. data/docs/sigs.md +3 -3
  65. data/ext/rbs_extension/ast_translation.c +1077 -944
  66. data/ext/rbs_extension/ast_translation.h +0 -7
  67. data/ext/rbs_extension/class_constants.c +83 -71
  68. data/ext/rbs_extension/class_constants.h +7 -4
  69. data/ext/rbs_extension/extconf.rb +2 -24
  70. data/ext/rbs_extension/legacy_location.c +172 -173
  71. data/ext/rbs_extension/legacy_location.h +3 -8
  72. data/ext/rbs_extension/main.c +289 -239
  73. data/ext/rbs_extension/rbs_extension.h +0 -3
  74. data/ext/rbs_extension/rbs_string_bridging.h +0 -4
  75. data/include/rbs/ast.h +98 -37
  76. data/include/rbs/defines.h +12 -38
  77. data/include/rbs/lexer.h +114 -126
  78. data/include/rbs/location.h +14 -14
  79. data/include/rbs/parser.h +37 -21
  80. data/include/rbs/string.h +5 -3
  81. data/include/rbs/util/rbs_allocator.h +19 -40
  82. data/include/rbs/util/rbs_assert.h +1 -12
  83. data/include/rbs/util/rbs_constant_pool.h +3 -3
  84. data/include/rbs/util/rbs_encoding.h +1 -3
  85. data/include/rbs/util/rbs_unescape.h +1 -2
  86. data/lib/rbs/ast/ruby/annotations.rb +119 -0
  87. data/lib/rbs/ast/ruby/comment_block.rb +221 -0
  88. data/lib/rbs/ast/ruby/declarations.rb +86 -0
  89. data/lib/rbs/ast/ruby/helpers/constant_helper.rb +24 -0
  90. data/lib/rbs/ast/ruby/helpers/location_helper.rb +15 -0
  91. data/lib/rbs/ast/ruby/members.rb +213 -0
  92. data/lib/rbs/buffer.rb +104 -24
  93. data/lib/rbs/cli/validate.rb +40 -35
  94. data/lib/rbs/cli.rb +5 -6
  95. data/lib/rbs/collection/config/lockfile_generator.rb +0 -1
  96. data/lib/rbs/collection.rb +0 -1
  97. data/lib/rbs/definition.rb +6 -1
  98. data/lib/rbs/definition_builder/ancestor_builder.rb +65 -62
  99. data/lib/rbs/definition_builder/method_builder.rb +45 -30
  100. data/lib/rbs/definition_builder.rb +44 -9
  101. data/lib/rbs/environment/class_entry.rb +69 -0
  102. data/lib/rbs/environment/module_entry.rb +66 -0
  103. data/lib/rbs/environment.rb +244 -218
  104. data/lib/rbs/environment_loader.rb +3 -3
  105. data/lib/rbs/errors.rb +5 -4
  106. data/lib/rbs/inline_parser/comment_association.rb +117 -0
  107. data/lib/rbs/inline_parser.rb +206 -0
  108. data/lib/rbs/location_aux.rb +35 -3
  109. data/lib/rbs/parser_aux.rb +11 -6
  110. data/lib/rbs/prototype/runtime.rb +2 -2
  111. data/lib/rbs/resolver/constant_resolver.rb +2 -2
  112. data/lib/rbs/resolver/type_name_resolver.rb +38 -124
  113. data/lib/rbs/source.rb +99 -0
  114. data/lib/rbs/subtractor.rb +4 -3
  115. data/lib/rbs/test/type_check.rb +0 -14
  116. data/lib/rbs/types.rb +1 -3
  117. data/lib/rbs/version.rb +1 -1
  118. data/lib/rbs.rb +13 -1
  119. data/lib/rdoc/discover.rb +1 -1
  120. data/lib/rdoc_plugin/parser.rb +1 -1
  121. data/rbs.gemspec +1 -0
  122. data/sig/ancestor_builder.rbs +1 -1
  123. data/sig/ast/ruby/annotations.rbs +110 -0
  124. data/sig/ast/ruby/comment_block.rbs +119 -0
  125. data/sig/ast/ruby/declarations.rbs +60 -0
  126. data/sig/ast/ruby/helpers/constant_helper.rbs +11 -0
  127. data/sig/ast/ruby/helpers/location_helper.rbs +15 -0
  128. data/sig/ast/ruby/members.rbs +72 -0
  129. data/sig/buffer.rbs +63 -5
  130. data/sig/definition.rbs +1 -0
  131. data/sig/definition_builder.rbs +1 -1
  132. data/sig/environment/class_entry.rbs +50 -0
  133. data/sig/environment/module_entry.rbs +50 -0
  134. data/sig/environment.rbs +28 -133
  135. data/sig/errors.rbs +13 -6
  136. data/sig/inline_parser/comment_association.rbs +71 -0
  137. data/sig/inline_parser.rbs +87 -0
  138. data/sig/location.rbs +32 -7
  139. data/sig/manifest.yaml +1 -0
  140. data/sig/method_builder.rbs +7 -4
  141. data/sig/parser.rbs +16 -20
  142. data/sig/resolver/type_name_resolver.rbs +7 -38
  143. data/sig/source.rbs +48 -0
  144. data/sig/types.rbs +1 -4
  145. data/src/ast.c +290 -201
  146. data/src/lexer.c +2813 -2902
  147. data/src/lexer.re +4 -0
  148. data/src/lexstate.c +155 -169
  149. data/src/location.c +40 -40
  150. data/src/parser.c +2665 -2433
  151. data/src/string.c +48 -0
  152. data/src/util/rbs_allocator.c +77 -80
  153. data/src/util/rbs_assert.c +10 -10
  154. data/src/util/rbs_buffer.c +2 -2
  155. data/src/util/rbs_constant_pool.c +15 -13
  156. data/src/util/rbs_encoding.c +4062 -20097
  157. data/src/util/rbs_unescape.c +48 -85
  158. data/stdlib/bigdecimal/0/big_decimal.rbs +82 -100
  159. data/stdlib/bigdecimal-math/0/big_math.rbs +8 -169
  160. data/stdlib/cgi/0/core.rbs +396 -2
  161. data/stdlib/cgi/0/manifest.yaml +0 -1
  162. data/stdlib/coverage/0/coverage.rbs +1 -3
  163. data/stdlib/date/0/date.rbs +59 -67
  164. data/stdlib/date/0/date_time.rbs +1 -1
  165. data/stdlib/delegate/0/delegator.rbs +7 -10
  166. data/stdlib/erb/0/erb.rbs +347 -737
  167. data/stdlib/fileutils/0/fileutils.rbs +13 -18
  168. data/stdlib/forwardable/0/forwardable.rbs +0 -3
  169. data/stdlib/json/0/json.rbs +48 -68
  170. data/stdlib/net-http/0/net-http.rbs +0 -3
  171. data/stdlib/objspace/0/objspace.rbs +4 -9
  172. data/stdlib/open-uri/0/open-uri.rbs +0 -40
  173. data/stdlib/openssl/0/openssl.rbs +228 -331
  174. data/stdlib/optparse/0/optparse.rbs +3 -3
  175. data/{core → stdlib/pathname/0}/pathname.rbs +355 -255
  176. data/stdlib/psych/0/psych.rbs +3 -3
  177. data/stdlib/rdoc/0/rdoc.rbs +1 -1
  178. data/stdlib/resolv/0/resolv.rbs +68 -25
  179. data/stdlib/ripper/0/ripper.rbs +2 -5
  180. data/stdlib/singleton/0/singleton.rbs +0 -3
  181. data/stdlib/socket/0/socket.rbs +1 -13
  182. data/stdlib/socket/0/tcp_socket.rbs +2 -10
  183. data/stdlib/stringio/0/stringio.rbs +85 -1176
  184. data/stdlib/strscan/0/string_scanner.rbs +31 -31
  185. data/stdlib/tempfile/0/tempfile.rbs +3 -3
  186. data/stdlib/time/0/time.rbs +1 -1
  187. data/stdlib/timeout/0/timeout.rbs +7 -63
  188. data/stdlib/tsort/0/cyclic.rbs +0 -3
  189. data/stdlib/uri/0/common.rbs +2 -11
  190. data/stdlib/uri/0/file.rbs +1 -1
  191. data/stdlib/uri/0/generic.rbs +16 -17
  192. data/stdlib/uri/0/rfc2396_parser.rbs +7 -6
  193. data/stdlib/zlib/0/zstream.rbs +0 -1
  194. metadata +40 -12
  195. data/.clang-format +0 -74
  196. data/.clangd +0 -2
  197. data/.github/workflows/c-check.yml +0 -54
  198. data/core/ruby.rbs +0 -53
  199. data/docs/aliases.md +0 -79
  200. data/docs/encoding.md +0 -56
  201. data/ext/rbs_extension/compat.h +0 -10
  202. data/stdlib/cgi-escape/0/escape.rbs +0 -153
data/core/ractor.rbs CHANGED
@@ -1,9 +1,12 @@
1
1
  # <!-- rdoc-file=ractor.rb -->
2
+ # Ractor is an Actor-model abstraction for Ruby that provides thread-safe
3
+ # parallel execution.
4
+ #
2
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.join # wait for it to finish
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
@@ -37,12 +40,55 @@
37
40
  # puts "I am in Ractor! a=#{a_in_ractor}"
38
41
  # end
39
42
  # r.send(a) # pass it
40
- # r.join
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 can be received with Ractor#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
  #
@@ -82,7 +128,7 @@
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.join
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:
@@ -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.join
154
+ # r.take
109
155
  # puts "Outside: moved? #{Ractor::MovedObject === data}"
110
156
  # puts "Outside: #{data.inspect}"
111
157
  #
@@ -118,7 +164,7 @@
118
164
  # Notice that even `inspect` (and more basic methods like `__id__`) is
119
165
  # inaccessible on a moved object.
120
166
  #
121
- # `Class` and `Module` objects are shareable so the class/module definitions are
167
+ # Class and Module objects are shareable so the class/module definitions are
122
168
  # shared between ractors. Ractor objects are also shareable. All operations on
123
169
  # shareable objects are thread-safe, so the thread-safety property will be kept.
124
170
  # We can not define mutable shareable objects in Ruby, but C extensions can
@@ -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.join
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.join
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.join
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,7 +229,7 @@
183
229
  # a = 1
184
230
  # Thread.new {puts "Thread in ractor: a=#{a}"}.join
185
231
  # end
186
- # r.join
232
+ # r.take
187
233
  # # Here "Thread in ractor: a=1" will be printed
188
234
  #
189
235
  # ## Note on code examples
@@ -196,7 +242,7 @@
196
242
  # end
197
243
  #
198
244
  # It 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.
245
+ # code. Most of the time, #take is used to wait for ractors to finish.
200
246
  #
201
247
  # ## Reference
202
248
  #
@@ -234,10 +280,10 @@ class Ractor
234
280
  # Returns the number of Ractors currently running or blocking (waiting).
235
281
  #
236
282
  # Ractor.count #=> 1
237
- # r = Ractor.new(name: 'example') { Ractor.receive }
283
+ # r = Ractor.new(name: 'example') { Ractor.yield(1) }
238
284
  # Ractor.count #=> 2 (main + example ractor)
239
- # r << 42 # r's Ractor.receive will resume
240
- # r.join # wait for r's termination
285
+ # r.take # wait for Ractor.yield(1)
286
+ # r.take # wait until r will finish
241
287
  # Ractor.count #=> 1
242
288
  #
243
289
  def self.count: () -> Integer
@@ -317,7 +363,7 @@ class Ractor
317
363
  # `self` inside the block will refer to the current Ractor.
318
364
  #
319
365
  # r = Ractor.new { puts "Hi, I am #{self.inspect}" }
320
- # r.join
366
+ # r.take
321
367
  # # Prints "Hi, I am #<Ractor:#2 test.rb:1 running>"
322
368
  #
323
369
  # Any `args` passed are propagated to the block arguments by the same rules as
@@ -329,14 +375,14 @@ class Ractor
329
375
  # r = Ractor.new(arg) {|received_arg|
330
376
  # puts "Received: #{received_arg} (##{received_arg.object_id})"
331
377
  # }
332
- # r.join
378
+ # r.take
333
379
  # # Prints:
334
380
  # # Passing: [1, 2, 3] (#280)
335
381
  # # Received: [1, 2, 3] (#300)
336
382
  #
337
383
  # Ractor's `name` can be set for debugging purposes:
338
384
  #
339
- # r = Ractor.new(name: 'my ractor') {}; r.join
385
+ # r = Ractor.new(name: 'my ractor') {}; r.take
340
386
  # p r
341
387
  # #=> #<Ractor:#3 my ractor test.rb:1 terminated>
342
388
  #
@@ -344,77 +390,209 @@ class Ractor
344
390
 
345
391
  # <!--
346
392
  # rdoc-file=ractor.rb
347
- # - Ractor.receive -> obj
393
+ # - Ractor.receive -> msg
348
394
  # -->
349
- # Receive a message from the default port.
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)
350
450
  #
351
451
  def self.receive: () -> untyped
352
452
 
353
453
  # <!--
354
454
  # rdoc-file=ractor.rb
355
- # - recv()
455
+ # - Ractor.receive_if {|msg| block } -> msg
356
456
  # -->
457
+ # Receive only a specific message.
357
458
  #
358
- alias self.recv self.receive
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
359
514
 
360
515
  # <!--
361
516
  # rdoc-file=ractor.rb
362
- # - Ractor.select(*ports) -> [...]
517
+ # - recv()
363
518
  # -->
364
- # TBD
365
519
  #
366
- def self.select: (?) -> Array[untyped]
520
+ alias self.recv self.receive
367
521
 
368
522
  # <!--
369
523
  # rdoc-file=ractor.rb
370
- # - Ractor.shareable?(obj) -> true | false
524
+ # - Ractor.select(*ractors, [yield_value:, move: false]) -> [ractor or symbol, obj]
371
525
  # -->
372
- # Checks if the object is shareable by ractors.
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.
373
528
  #
374
- # Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are frozen
375
- # Ractor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true
376
- # Ractor.shareable?('foo'.freeze) #=> true
529
+ # r1 = Ractor.new {Ractor.yield 'from 1'}
530
+ # r2 = Ractor.new {Ractor.yield 'from 2'}
377
531
  #
378
- # See also the "Shareable and unshareable objects" section in the Ractor class
379
- # docs.
532
+ # r, obj = Ractor.select(r1, r2)
380
533
  #
381
- def self.shareable?: (untyped obj) -> bool
382
-
383
- # <!--
384
- # rdoc-file=ractor.rb
385
- # - Ractor.shareable_proc(self: nil){} -> shareable proc
386
- # -->
387
- # It returns shareable Proc object. The Proc object is shareable and the self in
388
- # a block will be replaced with the value passed via `self:` keyword.
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.
537
+ #
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'
547
+ # end
548
+ #
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
552
+ #
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:
389
555
  #
390
- # In a shareable Proc, you can not access to the outer variables.
556
+ # r1 = Ractor.new(Ractor.current) do |main|
557
+ # puts "Received from main: #{main.take}"
558
+ # end
559
+ #
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}"
391
564
  #
392
- # a = 42
393
- # Ractor.shareable_proc{ p a }
394
- # #=> can not isolate a Proc because it accesses outer variables (a). (ArgumentError)
565
+ # This will print:
395
566
  #
396
- # The `self` should be a shareable object
567
+ # Trying to select
568
+ # Received from main: 123
569
+ # Received nil from :yield
397
570
  #
398
- # Ractor.shareable_proc(self: self){}
399
- # #=> self should be shareable: main (Ractor::IsolationError)
571
+ # `move` boolean flag defines whether yielded value will be copied (default) or
572
+ # moved.
400
573
  #
401
- def self.shareable_proc: [T] () { (?) [self: nil] -> T } -> ^(?) [self: nil] -> T
402
- | [T, S] (self: S) { (?) [self: S] -> T } -> ^(?) [self: S] -> T
574
+ def self.select: (*Ractor ractors, ?move: boolish, ?yield_value: untyped) -> [ Ractor | Symbol, untyped ]
403
575
 
404
576
  # <!--
405
577
  # rdoc-file=ractor.rb
406
- # - Ractor.shareable_proc{} -> shareable proc
578
+ # - Ractor.shareable?(obj) -> true | false
407
579
  # -->
408
- # Same as Ractor.shareable_proc, but returns lambda proc.
580
+ # Checks if the object is shareable by ractors.
409
581
  #
410
- def self.shareable_lambda: [T] () { (?) [self: nil] -> T } -> ^(?) [self: nil] -> T
411
- | [T, S] (self: S) { (?) [self: S] -> T } -> ^(?) [self: S] -> T
582
+ # Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are frozen
583
+ # Ractor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true
584
+ # Ractor.shareable?('foo'.freeze) #=> true
585
+ #
586
+ # See also the "Shareable and unshareable objects" section in the Ractor class
587
+ # docs.
588
+ #
589
+ def self.shareable?: (untyped obj) -> bool
412
590
 
413
591
  # <!--
414
592
  # rdoc-file=ractor.rb
415
593
  # - Ractor.store_if_absent(key){ init_block }
416
594
  # -->
417
- # If the corresponding value is not set, yield a value with init_block and store
595
+ # If the correponding value is not set, yield a value with init_block and store
418
596
  # the value in thread-safe manner. This method returns corresponding stored
419
597
  # value.
420
598
  #
@@ -429,7 +607,47 @@ class Ractor
429
607
 
430
608
  # <!--
431
609
  # rdoc-file=ractor.rb
432
- # - <<(...)
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)
433
651
  # -->
434
652
  #
435
653
  alias << send
@@ -438,50 +656,58 @@ class Ractor
438
656
  # rdoc-file=ractor.rb
439
657
  # - [](sym)
440
658
  # -->
441
- # get a value from ractor-local storage for current Ractor Obsolete and use
659
+ # get a value from ractor-local storage of current Ractor Obsolete and use
442
660
  # Ractor.[] instead.
443
661
  #
444
- %a{deprecated: Use Ractor.[] instead}
445
662
  def []: (interned sym) -> untyped
446
663
 
447
664
  # <!--
448
665
  # rdoc-file=ractor.rb
449
666
  # - []=(sym, val)
450
667
  # -->
451
- # set a value in ractor-local storage for current Ractor Obsolete and use
668
+ # set a value in ractor-local storage of current Ractor Obsolete and use
452
669
  # Ractor.[]= instead.
453
670
  #
454
- %a{deprecated: Use Ractor.[]= instead}
455
671
  def []=: [T] (interned sym, T val) -> T
456
672
 
457
673
  # <!--
458
674
  # rdoc-file=ractor.rb
459
- # - ractor.default_port -> port object
675
+ # - ractor.close_incoming -> true | false
460
676
  # -->
461
- # return default port of the Ractor.
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.
680
+ #
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)
462
686
  #
463
- def default_port: () -> Port[untyped]
687
+ def close_incoming: () -> bool
464
688
 
465
689
  # <!--
466
690
  # rdoc-file=ractor.rb
467
- # - inspect()
691
+ # - ractor.close_outgoing -> true | false
468
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.
469
696
  #
470
- def inspect: () -> String
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
471
704
 
472
705
  # <!--
473
706
  # rdoc-file=ractor.rb
474
- # - ractor.join -> self
707
+ # - inspect()
475
708
  # -->
476
- # Wait for the termination of the Ractor. If the Ractor was aborted (terminated
477
- # with an exception), Ractor#value is called to raise an exception.
478
709
  #
479
- # Ractor.new{}.join #=> ractor
480
- #
481
- # Ractor.new{ raise "foo" }.join
482
- # #=> raise an exception "foo (RuntimeError)"
483
- #
484
- def join: () -> self
710
+ def inspect: () -> String
485
711
 
486
712
  # <!--
487
713
  # rdoc-file=ractor.rb
@@ -493,60 +719,162 @@ class Ractor
493
719
 
494
720
  # <!--
495
721
  # rdoc-file=ractor.rb
496
- # - ractor.monitor(port) -> self
722
+ # - ractor.send(msg, move: false) -> self
497
723
  # -->
498
- # Register port as a monitoring port. If the ractor terminated, the port
499
- # received a Symbol object. :exited will be sent if the ractor terminated
500
- # without an exception. :aborted will be sent if the ractor terminated with a
501
- # exception.
724
+ # Send a message to a Ractor's incoming queue to be accepted by Ractor.receive.
502
725
  #
503
- # r = Ractor.new{ some_task() }
504
- # r.monitor(port = Ractor::Port.new)
505
- # port.receive #=> :exited and r is terminated
726
+ # r = Ractor.new do
727
+ # value = Ractor.receive
728
+ # puts "Received #{value}"
729
+ # end
730
+ # r.send 'message'
731
+ # # Prints: "Received: message"
506
732
  #
507
- # r = Ractor.new{ raise "foo" }
508
- # r.monitor(port = Ractor::Port.new)
509
- # port.receive #=> :terminated and r is terminated with an exception "foo"
733
+ # The method is non-blocking (will return immediately even if the ractor is not
734
+ # ready to receive anything):
510
735
  #
511
- def monitor: [T < Symbol] (Port[T]) -> untyped
512
-
513
- # <!--
514
- # rdoc-file=ractor.rb
515
- # - ractor.send(msg) -> self
516
- # -->
517
- # It is equivalent to default_port.send(msg)
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
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
518
802
  #
519
803
  def send: (untyped obj, ?move: boolish) -> Ractor
520
804
 
521
805
  # <!--
522
806
  # rdoc-file=ractor.rb
523
- # - to_s()
807
+ # - ractor.take -> msg
524
808
  # -->
809
+ # Get a message from the ractor's outgoing port, which was put there by
810
+ # Ractor.yield or at ractor's termination.
525
811
  #
526
- alias to_s inspect
527
-
528
- # <!--
529
- # rdoc-file=ractor.rb
530
- # - ractor.unmonitor(port) -> self
531
- # -->
532
- # Unregister port from the monitoring ports.
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.
533
827
  #
534
- def unmonitor: (Port[untyped]) -> self
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
+ #
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
535
871
 
536
872
  # <!--
537
873
  # rdoc-file=ractor.rb
538
- # - ractor.value -> obj
874
+ # - to_s()
539
875
  # -->
540
- # Waits for `ractor` to complete, using #join, and return its value or raise the
541
- # exception which terminated the Ractor. The value will not be copied even if it
542
- # is unshareable object. Therefore at most 1 Ractor can get a value.
543
- #
544
- # r = Ractor.new{ [1, 2] }
545
- # r.value #=> [1, 2] (unshareable object)
546
- #
547
- # Ractor.new(r){|r| r.value} #=> Ractor::Error
548
876
  #
549
- def value: () -> untyped
877
+ alias to_s inspect
550
878
 
551
879
  private
552
880
 
@@ -610,15 +938,9 @@ class Ractor
610
938
  class ClosedError < StopIteration
611
939
  end
612
940
 
613
- # <!-- rdoc-file=ractor.c -->
614
- # The parent class of Ractor-related error classes.
615
- #
616
941
  class Error < RuntimeError
617
942
  end
618
943
 
619
- # <!-- rdoc-file=ractor.c -->
620
- # Raised on attempt to make a Ractor-unshareable object Ractor-shareable.
621
- #
622
944
  class IsolationError < Ractor::Error
623
945
  end
624
946
 
@@ -715,166 +1037,6 @@ class Ractor
715
1037
  def method_missing: (*untyped) -> untyped
716
1038
  end
717
1039
 
718
- # <!-- rdoc-file=ractor.rb -->
719
- # Port objects transmit messages between Ractors.
720
- #
721
- class Port[T = untyped]
722
- # <!--
723
- # rdoc-file=ractor.rb
724
- # - <<(obj, move: false)
725
- # -->
726
- #
727
- alias << send
728
-
729
- # <!--
730
- # rdoc-file=ractor.rb
731
- # - port.close
732
- # -->
733
- # Close the port. On the closed port, sending is not prohibited. Receiving is
734
- # also not allowed if there is no sent messages arrived before closing.
735
- #
736
- # port = Ractor::Port.new
737
- # Ractor.new port do |port|
738
- # port.send 1 # OK
739
- # port.send 2 # OK
740
- # port.close
741
- # port.send 3 # raise Ractor::ClosedError
742
- # end
743
- #
744
- # port.receive #=> 1
745
- # port.receive #=> 2
746
- # port.receive #=> raise Ractor::ClosedError
747
- #
748
- # Now, only a Ractor which creates the port is allowed to close ports.
749
- #
750
- # port = Ractor::Port.new
751
- # Ractor.new port do |port|
752
- # port.close #=> closing port by other ractors is not allowed (Ractor::Error)
753
- # end.join
754
- #
755
- def close: () -> void
756
-
757
- # <!--
758
- # rdoc-file=ractor.rb
759
- # - port.closed? -> true/false
760
- # -->
761
- # Return the port is closed or not.
762
- #
763
- def closed?: () -> bool
764
-
765
- # <!--
766
- # rdoc-file=ractor.rb
767
- # - port.inspect -> string
768
- # -->
769
- #
770
- def inspect: () -> String
771
-
772
- # <!--
773
- # rdoc-file=ractor.rb
774
- # - port.receive -> msg
775
- # -->
776
- # Receive a message to the port (which was sent there by Port#send).
777
- #
778
- # port = Ractor::Port.new
779
- # r = Ractor.new port do |port|
780
- # port.send('message1')
781
- # end
782
- #
783
- # v1 = port.receive
784
- # puts "Received: #{v1}"
785
- # r.join
786
- # # Here will be printed: "Received: message1"
787
- #
788
- # The method blocks if the message queue is empty.
789
- #
790
- # port = Ractor::Port.new
791
- # r = Ractor.new port do |port|
792
- # wait
793
- # puts "Still not received"
794
- # port.send('message1')
795
- # wait
796
- # puts "Still received only one"
797
- # port.send('message2')
798
- # end
799
- # puts "Before first receive"
800
- # v1 = port.receive
801
- # puts "Received: #{v1}"
802
- # v2 = port.receive
803
- # puts "Received: #{v2}"
804
- # r.join
805
- #
806
- # Output:
807
- #
808
- # Before first receive
809
- # Still not received
810
- # Received: message1
811
- # Still received only one
812
- # Received: message2
813
- #
814
- # If close_incoming was called on the ractor, the method raises
815
- # Ractor::ClosedError if there are no more messages in the message queue:
816
- #
817
- # port = Ractor::Port.new
818
- # port.close
819
- # port.receive #=> raise Ractor::ClosedError
820
- #
821
- def receive: () -> T
822
-
823
- # <!--
824
- # rdoc-file=ractor.rb
825
- # - port.send(msg, move: false) -> self
826
- # -->
827
- # Send a message to a port to be accepted by port.receive.
828
- #
829
- # port = Ractor::Port.new
830
- # r = Ractor.new do
831
- # r.send 'message'
832
- # end
833
- # value = port.receive
834
- # puts "Received #{value}"
835
- # # Prints: "Received: message"
836
- #
837
- # The method is non-blocking (will return immediately even if the ractor is not
838
- # ready to receive anything):
839
- #
840
- # port = Ractor::Port.new
841
- # r = Ractor.new(port) do |port|
842
- # port.send 'test'}
843
- # puts "Sent successfully"
844
- # # Prints: "Sent successfully" immediately
845
- # end
846
- #
847
- # An attempt to send to a port which already closed its execution will raise
848
- # Ractor::ClosedError.
849
- #
850
- # r = Ractor.new {Ractor::Port.new}
851
- # r.join
852
- # p r
853
- # # "#<Ractor:#6 (irb):23 terminated>"
854
- # port = r.value
855
- # port.send('test') # raise Ractor::ClosedError
856
- #
857
- # If the `obj` is unshareable, by default it will be copied into the receiving
858
- # ractor by deep cloning.
859
- #
860
- # If the object is shareable, it only send a reference to the object without
861
- # cloning.
862
- #
863
- def send: (T obj, ?move: boolish) -> self
864
-
865
- private
866
-
867
- # <!--
868
- # rdoc-file=ractor_sync.c
869
- # - Ractor::Port.new -> new_port
870
- # -->
871
- # Returns a new Ractor::Port object.
872
- #
873
- def initialize: () -> void
874
-
875
- def initialize_copy: (untyped) -> untyped
876
- end
877
-
878
1040
  # <!-- rdoc-file=ractor.c -->
879
1041
  # Raised on attempt to Ractor#take if there was an uncaught exception in the
880
1042
  # Ractor. Its `cause` will contain the original exception, and `ractor` is the
@@ -897,9 +1059,6 @@ class Ractor
897
1059
  def ractor: () -> Ractor
898
1060
  end
899
1061
 
900
- # <!-- rdoc-file=ractor.c -->
901
- # Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
902
- #
903
1062
  class UnsafeError < Ractor::Error
904
1063
  end
905
1064