# <!-- rdoc-file=ractor.rb -->
# Ractor is a Actor-model abstraction for Ruby that provides thread-safe
# parallel execution.
#
# Ractor.new can make a new Ractor, and it will run in parallel.
#
#     # The simplest ractor
#     r = Ractor.new {puts "I am in Ractor!"}
#     r.take # wait for it to finish
#     # here "I am in Ractor!" would be printed
#
# Ractors do not share usual objects, so the same kinds of thread-safety
# concerns such as data-race, race-conditions are not available on multi-ractor
# programming.
#
# To achieve this, ractors severely limit object sharing between different
# ractors. For example, unlike threads, ractors can't access each other's
# objects, nor any objects through variables of the outer scope.
#
#     a = 1
#     r = Ractor.new {puts "I am in Ractor! a=#{a}"}
#     # fails immediately with
#     # ArgumentError (can not isolate a Proc because it accesses outer variables (a).)
#
# On CRuby (the default implementation), Global Virtual Machine Lock (GVL) is
# held per ractor, so ractors are performed in parallel without locking each
# other.
#
# Instead of accessing the shared state, the objects should be passed to and
# from ractors via sending and receiving objects as messages.
#
#     a = 1
#     r = Ractor.new do
#       a_in_ractor = receive # receive blocks till somebody will pass message
#       puts "I am in Ractor! a=#{a_in_ractor}"
#     end
#     r.send(a)  # pass it
#     r.take
#     # here "I am in Ractor! a=1" would be printed
#
# There are two pairs of methods for sending/receiving messages:
#
# *   Ractor#send and Ractor.receive for when the *sender* knows the receiver
#     (push);
# *   Ractor.yield and Ractor#take for when the *receiver* knows the sender
#     (pull);
#
#
# In addition to that, an argument to Ractor.new would be passed to block and
# available there as if received by Ractor.receive, and the last block value
# would be sent outside of the ractor as if sent by Ractor.yield.
#
# A little demonstration on a classic ping-pong:
#
#     server = Ractor.new do
#       puts "Server starts: #{self.inspect}"
#       puts "Server sends: ping"
#       Ractor.yield 'ping'                       # The server doesn't know the receiver and sends to whoever interested
#       received = Ractor.receive                 # The server doesn't know the sender and receives from whoever sent
#       puts "Server received: #{received}"
#     end
#
#     client = Ractor.new(server) do |srv|        # The server is sent inside client, and available as srv
#       puts "Client starts: #{self.inspect}"
#       received = srv.take                       # The Client takes a message specifically from the server
#       puts "Client received from " \
#            "#{srv.inspect}: #{received}"
#       puts "Client sends to " \
#            "#{srv.inspect}: pong"
#       srv.send 'pong'                           # The client sends a message specifically to the server
#     end
#
#     [client, server].each(&:take)               # Wait till they both finish
#
# This will output:
#
#     Server starts: #<Ractor:#2 test.rb:1 running>
#     Server sends: ping
#     Client starts: #<Ractor:#3 test.rb:8 running>
#     Client received from #<Ractor:#2 rac.rb:1 blocking>: ping
#     Client sends to #<Ractor:#2 rac.rb:1 blocking>: pong
#     Server received: pong
#
# It is said that Ractor receives messages via the *incoming port*, and sends
# them to the *outgoing port*. Either one can be disabled with
# Ractor#close_incoming and Ractor#close_outgoing respectively. If a ractor
# terminated, its ports will be closed automatically.
#
# ## Shareable and unshareable objects
#
# When the object is sent to and from the ractor, it is important to understand
# whether the object is shareable or unshareable. Most of objects are
# unshareable objects.
#
# Shareable objects are basically those which can be used by several threads
# without compromising thread-safety; e.g. immutable ones. Ractor.shareable?
# allows to check this, and Ractor.make_shareable tries to make object shareable
# if it is not.
#
#     Ractor.shareable?(1)            #=> true -- numbers and other immutable basic values are
#     Ractor.shareable?('foo')        #=> false, unless the string is frozen due to # freeze_string_literals: true
#     Ractor.shareable?('foo'.freeze) #=> true
#
#     ary = ['hello', 'world']
#     ary.frozen?                 #=> false
#     ary[0].frozen?              #=> false
#     Ractor.make_shareable(ary)
#     ary.frozen?                 #=> true
#     ary[0].frozen?              #=> true
#     ary[1].frozen?              #=> true
#
# When a shareable object is sent (via #send or Ractor.yield), no additional
# processing happens, and it just becomes usable by both ractors. When an
# unshareable object is sent, it can be either *copied* or *moved*. The first is
# the default, and it makes the object's full copy by deep cloning of
# non-shareable parts of its structure.
#
#     data = ['foo', 'bar'.freeze]
#     r = Ractor.new do
#       data2 = Ractor.receive
#       puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}"
#     end
#     r.send(data)
#     r.take
#     puts "Outside  : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
#
# This will output:
#
#     In ractor: 340, 360, 320
#     Outside  : 380, 400, 320
#
# (Note that object id of both array and non-frozen string inside array have
# changed inside the ractor, showing it is different objects. But the second
# array's element, which is a shareable frozen string, has the same object_id.)
#
# Deep cloning of the objects may be slow, and sometimes impossible.
# Alternatively, `move: true` may be used on sending. This will *move* the
# object to the receiving ractor, making it inaccessible for a sending ractor.
#
#     data = ['foo', 'bar']
#     r = Ractor.new do
#       data_in_ractor = Ractor.receive
#       puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}"
#     end
#     r.send(data, move: true)
#     r.take
#     puts "Outside: moved? #{Ractor::MovedObject === data}"
#     puts "Outside: #{data.inspect}"
#
# This will output:
#
#     In ractor: 100, 120
#     Outside: moved? true
#     test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
#
# Notice that even `inspect` (and more basic methods like `__id__`) is
# inaccessible on a moved object.
#
# Besides frozen objects, there are shareable objects. Class and Module objects
# are shareable so the Class/Module definitions are shared between ractors.
# Ractor objects are also shareable objects. All operations for the shareable
# mutable objects are thread-safe, so the thread-safety property will be kept.
# We can not define mutable shareable objects in Ruby, but C extensions can
# introduce them.
#
# It is prohibited to access instance variables of mutable shareable objects
# (especially Modules and classes) from ractors other than main:
#
#     class C
#       class << self
#         attr_accessor :tricky
#       end
#     end
#
#     C.tricky = 'test'
#
#     r = Ractor.new(C) do |cls|
#       puts "I see #{cls}"
#       puts "I can't see #{cls.tricky}"
#     end
#     r.take
#     # I see C
#     # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
#
# Ractors can access constants if they are shareable. The main Ractor is the
# only one that can access non-shareable constants.
#
#     GOOD = 'good'.freeze
#     BAD = 'bad'
#
#     r = Ractor.new do
#       puts "GOOD=#{GOOD}"
#       puts "BAD=#{BAD}"
#     end
#     r.take
#     # GOOD=good
#     # can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError)
#
#     # Consider the same C class from above
#
#     r = Ractor.new do
#       puts "I see #{C}"
#       puts "I can't see #{C.tricky}"
#     end
#     r.take
#     # I see C
#     # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
#
# See also the description of `# shareable_constant_value` pragma in [Comments
# syntax](rdoc-ref:syntax/comments.rdoc) explanation.
#
# ## Ractors vs threads
#
# Each ractor creates its own thread. New threads can be created from inside
# ractor (and, on CRuby, sharing GVL with other threads of this ractor).
#
#     r = Ractor.new do
#       a = 1
#       Thread.new {puts "Thread in ractor: a=#{a}"}.join
#     end
#     r.take
#     # Here "Thread in ractor: a=1" will be printed
#
# ## Note on code examples
#
# In examples below, sometimes we use the following method to wait till ractors
# that are not currently blocked will finish (or process till next blocking)
# method.
#
#     def wait
#       sleep(0.1)
#     end
#
# It is **only for demonstration purposes** and shouldn't be used in a real
# code. Most of the times, just #take is used to wait till ractor will finish.
#
# ## Reference
#
# See [Ractor design doc](rdoc-ref:ractor.md) for more details.
#
class Ractor
  # <!--
  #   rdoc-file=ractor.rb
  #   - count()
  # -->
  # Returns total count of Ractors currently running.
  #
  #     Ractor.count                   #=> 1
  #     r = Ractor.new(name: 'example') { Ractor.yield(1) }
  #     Ractor.count                   #=> 2 (main + example ractor)
  #     r.take                         # wait for Ractor.yield(1)
  #     r.take                         # wait till r will finish
  #     Ractor.count                   #=> 1
  #
  def self.count: () -> Integer

  # <!--
  #   rdoc-file=ractor.rb
  #   - current()
  # -->
  # Returns the currently executing Ractor.
  #
  #     Ractor.current #=> #<Ractor:#1 running>
  #
  def self.current: () -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - main()
  # -->
  # returns main ractor
  #
  def self.main: () -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.make_shareable(obj, copy: false) -> shareable_obj
  # -->
  # Make `obj` shareable between ractors.
  #
  # `obj` and all the objects it refers to will be frozen, unless they are already
  # shareable.
  #
  # If `copy` keyword is `true`, the method will copy objects before freezing them
  # This is safer option but it can take be slower.
  #
  # Note that the specification and implementation of this method are not mature
  # and may be changed in the future.
  #
  #     obj = ['test']
  #     Ractor.shareable?(obj)     #=> false
  #     Ractor.make_shareable(obj) #=> ["test"]
  #     Ractor.shareable?(obj)     #=> true
  #     obj.frozen?                #=> true
  #     obj[0].frozen?             #=> true
  #
  #     # Copy vs non-copy versions:
  #     obj1 = ['test']
  #     obj1s = Ractor.make_shareable(obj1)
  #     obj1.frozen?                        #=> true
  #     obj1s.object_id == obj1.object_id   #=> true
  #     obj2 = ['test']
  #     obj2s = Ractor.make_shareable(obj2, copy: true)
  #     obj2.frozen?                        #=> false
  #     obj2s.frozen?                       #=> true
  #     obj2s.object_id == obj2.object_id   #=> false
  #     obj2s[0].object_id == obj2[0].object_id #=> false
  #
  # See also the "Shareable and unshareable objects" section in the Ractor class
  # docs.
  #
  def self.make_shareable: [T] (T obj, ?copy: boolish) -> T

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.new(*args, name: nil) {|*args| block } -> ractor
  # -->
  # Create a new Ractor with args and a block.
  #
  # A block (Proc) will be isolated (can't access to outer variables). `self`
  # inside the block will refer to the current Ractor.
  #
  #     r = Ractor.new { puts "Hi, I am #{self.inspect}" }
  #     r.take
  #     # Prints "Hi, I am #<Ractor:#2 test.rb:1 running>"
  #
  # `args` passed to the method would be propagated to block args by the same
  # rules as objects passed through #send/Ractor.receive: if `args` are not
  # shareable, they will be copied (via deep cloning, which might be inefficient).
  #
  #     arg = [1, 2, 3]
  #     puts "Passing: #{arg} (##{arg.object_id})"
  #     r = Ractor.new(arg) {|received_arg|
  #       puts "Received: #{received_arg} (##{received_arg.object_id})"
  #     }
  #     r.take
  #     # Prints:
  #     #   Passing: [1, 2, 3] (#280)
  #     #   Received: [1, 2, 3] (#300)
  #
  # Ractor's `name` can be set for debugging purposes:
  #
  #     r = Ractor.new(name: 'my ractor') {}
  #     p r
  #     #=> #<Ractor:#3 my ractor test.rb:1 terminated>
  #
  def self.new: (*untyped args, ?name: string) { (*untyped) -> untyped } -> Ractor

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.receive -> msg
  # -->
  # Receive an incoming message from the current Ractor's incoming port's queue,
  # which was sent there by #send.
  #
  #     r = Ractor.new do
  #       v1 = Ractor.receive
  #       puts "Received: #{v1}"
  #     end
  #     r.send('message1')
  #     r.take
  #     # Here will be printed: "Received: message1"
  #
  # Alternatively, private instance method `receive` may be used:
  #
  #     r = Ractor.new do
  #       v1 = receive
  #       puts "Received: #{v1}"
  #     end
  #     r.send('message1')
  #     r.take
  #     # Here will be printed: "Received: message1"
  #
  # The method blocks if the queue is empty.
  #
  #     r = Ractor.new do
  #       puts "Before first receive"
  #       v1 = Ractor.receive
  #       puts "Received: #{v1}"
  #       v2 = Ractor.receive
  #       puts "Received: #{v2}"
  #     end
  #     wait
  #     puts "Still not received"
  #     r.send('message1')
  #     wait
  #     puts "Still received only one"
  #     r.send('message2')
  #     r.take
  #
  # Output:
  #
  #     Before first receive
  #     Still not received
  #     Received: message1
  #     Still received only one
  #     Received: message2
  #
  # If close_incoming was called on the ractor, the method raises
  # Ractor::ClosedError if there are no more messages in incoming queue:
  #
  #     Ractor.new do
  #       close_incoming
  #       receive
  #     end
  #     wait
  #     # in `receive': The incoming port is already closed => #<Ractor:#2 test.rb:1 running> (Ractor::ClosedError)
  #
  def self.receive: () -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.receive_if {|msg| block } -> msg
  # -->
  # Receive only a specific message.
  #
  # Instead of Ractor.receive, Ractor.receive_if can provide a pattern by a block
  # and you can choose the receiving message.
  #
  #     r = Ractor.new do
  #       p Ractor.receive_if{|msg| msg.match?(/foo/)} #=> "foo3"
  #       p Ractor.receive_if{|msg| msg.match?(/bar/)} #=> "bar1"
  #       p Ractor.receive_if{|msg| msg.match?(/baz/)} #=> "baz2"
  #     end
  #     r << "bar1"
  #     r << "baz2"
  #     r << "foo3"
  #     r.take
  #
  # This will output:
  #
  #     foo3
  #     bar1
  #     baz2
  #
  # If the block returns a truthy value, the message will be removed from the
  # incoming queue and returned. Otherwise, the message remains in the incoming
  # queue and the following received messages are checked by the given block.
  #
  # If there are no messages left in the incoming queue, the method will block
  # until new messages arrive.
  #
  # If the block is escaped by break/return/exception/throw, the message is
  # removed from the incoming queue as if a truthy value had been returned.
  #
  #     r = Ractor.new do
  #       val = Ractor.receive_if{|msg| msg.is_a?(Array)}
  #       puts "Received successfully: #{val}"
  #     end
  #
  #     r.send(1)
  #     r.send('test')
  #     wait
  #     puts "2 non-matching sent, nothing received"
  #     r.send([1, 2, 3])
  #     wait
  #
  # Prints:
  #
  #     2 non-matching sent, nothing received
  #     Received successfully: [1, 2, 3]
  #
  # Note that you can not call receive/receive_if in the given block recursively.
  # It means that you should not do any tasks in the block.
  #
  #     Ractor.current << true
  #     Ractor.receive_if{|msg| Ractor.receive}
  #     #=> `receive': can not call receive/receive_if recursively (Ractor::Error)
  #
  def self.receive_if: () { (untyped) -> boolish } -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - recv()
  # -->
  #
  alias self.recv self.receive

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.select(*ractors, [yield_value:, move: false]) -> [ractor or symbol, obj]
  # -->
  # Waits for the first ractor to have something in its outgoing port, reads from
  # this ractor, and returns that ractor and the object received.
  #
  #     r1 = Ractor.new {Ractor.yield 'from 1'}
  #     r2 = Ractor.new {Ractor.yield 'from 2'}
  #
  #     r, obj = Ractor.select(r1, r2)
  #
  #     puts "received #{obj.inspect} from #{r.inspect}"
  #     # Prints: received "from 1" from #<Ractor:#2 test.rb:1 running>
  #
  # If one of the given ractors is the current ractor, and it would be selected,
  # `r` will contain `:receive` symbol instead of the ractor object.
  #
  #     r1 = Ractor.new(Ractor.current) do |main|
  #       main.send 'to main'
  #       Ractor.yield 'from 1'
  #     end
  #     r2 = Ractor.new do
  #       Ractor.yield 'from 2'
  #     end
  #
  #     r, obj = Ractor.select(r1, r2, Ractor.current)
  #     puts "received #{obj.inspect} from #{r.inspect}"
  #     # Prints: received "to main" from :receive
  #
  # If `yield_value` is provided, that value may be yielded if another Ractor is
  # calling #take. In this case, the pair `[:yield, nil]` would be returned:
  #
  #     r1 = Ractor.new(Ractor.current) do |main|
  #       puts "Received from main: #{main.take}"
  #     end
  #
  #     puts "Trying to select"
  #     r, obj = Ractor.select(r1, Ractor.current, yield_value: 123)
  #     wait
  #     puts "Received #{obj.inspect} from #{r.inspect}"
  #
  # This will print:
  #
  #     Trying to select
  #     Received from main: 123
  #     Received nil from :yield
  #
  # `move` boolean flag defines whether yielded value should be copied (default)
  # or moved.
  #
  def self.select: (*Ractor ractors, ?move: boolish, ?yield_value: untyped) -> [ Ractor | Symbol, untyped ]

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.shareable?(obj) -> true | false
  # -->
  # Checks if the object is shareable by ractors.
  #
  #     Ractor.shareable?(1)            #=> true -- numbers and other immutable basic values are frozen
  #     Ractor.shareable?('foo')        #=> false, unless the string is frozen due to # freeze_string_literals: true
  #     Ractor.shareable?('foo'.freeze) #=> true
  #
  # See also the "Shareable and unshareable objects" section in the Ractor class
  # docs.
  #
  def self.shareable?: (untyped obj) -> bool

  # <!--
  #   rdoc-file=ractor.rb
  #   - Ractor.yield(msg, move: false) -> nil
  # -->
  # Send a message to the current ractor's outgoing port to be consumed by #take.
  #
  #     r = Ractor.new {Ractor.yield 'Hello from ractor'}
  #     puts r.take
  #     # Prints: "Hello from ractor"
  #
  # The method is blocking, and will return only when somebody consumes the sent
  # message.
  #
  #     r = Ractor.new do
  #       Ractor.yield 'Hello from ractor'
  #       puts "Ractor: after yield"
  #     end
  #     wait
  #     puts "Still not taken"
  #     puts r.take
  #
  # This will print:
  #
  #     Still not taken
  #     Hello from ractor
  #     Ractor: after yield
  #
  # If the outgoing port was closed with #close_outgoing, the method will raise:
  #
  #     r = Ractor.new do
  #       close_outgoing
  #       Ractor.yield 'Hello from ractor'
  #     end
  #     wait
  #     # `yield': The outgoing-port is already closed (Ractor::ClosedError)
  #
  # The meaning of `move` argument is the same as for #send.
  #
  def self.yield: (untyped obj, ?move: boolish) -> untyped

  public

  # <!--
  #   rdoc-file=ractor.rb
  #   - <<(obj, move: false)
  # -->
  #
  alias << send

  # <!--
  #   rdoc-file=ractor.rb
  #   - [](sym)
  # -->
  # get a value from ractor-local storage
  #
  def []: (Symbol | String sym) -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - []=(sym, val)
  # -->
  # set a value in ractor-local storage
  #
  def []=: [T] (Symbol | String sym, T val) -> T

  # <!--
  #   rdoc-file=ractor.rb
  #   - ractor.close_incoming -> true | false
  # -->
  # Closes the incoming port and returns its previous state. All further attempts
  # to Ractor.receive in the ractor, and #send to the ractor will fail with
  # Ractor::ClosedError.
  #
  #     r = Ractor.new {sleep(500)}
  #     r.close_incoming  #=> false
  #     r.close_incoming  #=> true
  #     r.send('test')
  #     # Ractor::ClosedError (The incoming-port is already closed)
  #
  def close_incoming: () -> bool

  # <!--
  #   rdoc-file=ractor.rb
  #   - ractor.close_outgoing -> true | false
  # -->
  # Closes the outgoing port and returns its previous state. All further attempts
  # to Ractor.yield in the ractor, and #take from the ractor will fail with
  # Ractor::ClosedError.
  #
  #     r = Ractor.new {sleep(500)}
  #     r.close_outgoing  #=> false
  #     r.close_outgoing  #=> true
  #     r.take
  #     # Ractor::ClosedError (The outgoing-port is already closed)
  #
  def close_outgoing: () -> bool

  # <!--
  #   rdoc-file=ractor.rb
  #   - inspect()
  # -->
  #
  def inspect: () -> String

  # <!--
  #   rdoc-file=ractor.rb
  #   - name()
  # -->
  # The name set in Ractor.new, or `nil`.
  #
  def name: () -> String?

  # <!--
  #   rdoc-file=ractor.rb
  #   - ractor.send(msg, move: false) -> self
  # -->
  # Send a message to a Ractor's incoming queue to be consumed by Ractor.receive.
  #
  #     r = Ractor.new do
  #       value = Ractor.receive
  #       puts "Received #{value}"
  #     end
  #     r.send 'message'
  #     # Prints: "Received: message"
  #
  # The method is non-blocking (will return immediately even if the ractor is not
  # ready to receive anything):
  #
  #     r = Ractor.new {sleep(5)}
  #     r.send('test')
  #     puts "Sent successfully"
  #     # Prints: "Sent successfully" immediately
  #
  # Attempt to send to ractor which already finished its execution will raise
  # Ractor::ClosedError.
  #
  #     r = Ractor.new {}
  #     r.take
  #     p r
  #     # "#<Ractor:#6 (irb):23 terminated>"
  #     r.send('test')
  #     # Ractor::ClosedError (The incoming-port is already closed)
  #
  # If close_incoming was called on the ractor, the method also raises
  # Ractor::ClosedError.
  #
  #     r =  Ractor.new do
  #       sleep(500)
  #       receive
  #     end
  #     r.close_incoming
  #     r.send('test')
  #     # Ractor::ClosedError (The incoming-port is already closed)
  #     # The error would be raised immediately, not when ractor will try to receive
  #
  # If the `obj` is unshareable, by default it would be copied into ractor by deep
  # cloning. If the `move: true` is passed, object is *moved* into ractor and
  # becomes inaccessible to sender.
  #
  #     r = Ractor.new {puts "Received: #{receive}"}
  #     msg = 'message'
  #     r.send(msg, move: true)
  #     r.take
  #     p msg
  #
  # This prints:
  #
  #     Received: message
  #     in `p': undefined method `inspect' for #<Ractor::MovedObject:0x000055c99b9b69b8>
  #
  # All references to the object and its parts will become invalid in sender.
  #
  #     r = Ractor.new {puts "Received: #{receive}"}
  #     s = 'message'
  #     ary = [s]
  #     copy = ary.dup
  #     r.send(ary, move: true)
  #
  #     s.inspect
  #     # Ractor::MovedError (can not send any methods to a moved object)
  #     ary.class
  #     # Ractor::MovedError (can not send any methods to a moved object)
  #     copy.class
  #     # => Array, it is different object
  #     copy[0].inspect
  #     # Ractor::MovedError (can not send any methods to a moved object)
  #     # ...but its item was still a reference to `s`, which was moved
  #
  # If the object was shareable, `move: true` has no effect on it:
  #
  #     r = Ractor.new {puts "Received: #{receive}"}
  #     s = 'message'.freeze
  #     r.send(s, move: true)
  #     s.inspect #=> "message", still available
  #
  def send: (untyped obj, ?move: boolish) -> Ractor

  # <!--
  #   rdoc-file=ractor.rb
  #   - ractor.take -> msg
  # -->
  # Take a message from ractor's outgoing port, which was put there by
  # Ractor.yield or at ractor's finalization.
  #
  #     r = Ractor.new do
  #       Ractor.yield 'explicit yield'
  #       'last value'
  #     end
  #     puts r.take #=> 'explicit yield'
  #     puts r.take #=> 'last value'
  #     puts r.take # Ractor::ClosedError (The outgoing-port is already closed)
  #
  # The fact that the last value is also put to outgoing port means that `take`
  # can be used as some analog of Thread#join ("just wait till ractor finishes"),
  # but don't forget it will raise if somebody had already consumed everything
  # ractor have produced.
  #
  # If the outgoing port was closed with #close_outgoing, the method will raise
  # Ractor::ClosedError.
  #
  #     r = Ractor.new do
  #       sleep(500)
  #       Ractor.yield 'Hello from ractor'
  #     end
  #     r.close_outgoing
  #     r.take
  #     # Ractor::ClosedError (The outgoing-port is already closed)
  #     # The error would be raised immediately, not when ractor will try to receive
  #
  # If an uncaught exception is raised in the Ractor, it is propagated on take as
  # a Ractor::RemoteError.
  #
  #     r = Ractor.new {raise "Something weird happened"}
  #
  #     begin
  #       r.take
  #     rescue => e
  #       p e              #  => #<Ractor::RemoteError: thrown by remote Ractor.>
  #       p e.ractor == r  # => true
  #       p e.cause        # => #<RuntimeError: Something weird happened>
  #     end
  #
  # Ractor::ClosedError is a descendant of StopIteration, so the closing of the
  # ractor will break the loops without propagating the error:
  #
  #     r = Ractor.new do
  #       3.times {|i| Ractor.yield "message #{i}"}
  #       "finishing"
  #     end
  #
  #     loop {puts "Received: " + r.take}
  #     puts "Continue successfully"
  #
  # This will print:
  #
  #     Received: message 0
  #     Received: message 1
  #     Received: message 2
  #     Received: finishing
  #     Continue successfully
  #
  def take: () -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - to_s()
  # -->
  #
  alias to_s inspect

  private

  # <!--
  #   rdoc-file=ractor.rb
  #   - receive()
  # -->
  # same as Ractor.receive
  #
  def receive: () -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - receive_if(&b)
  # -->
  #
  def receive_if: () { (untyped) -> boolish } -> untyped

  # <!--
  #   rdoc-file=ractor.rb
  #   - recv()
  # -->
  #
  alias recv receive

  # <!-- rdoc-file=ractor.c -->
  # Raised when an attempt is made to send a message to a closed port, or to
  # retrieve a message from a closed and empty port. Ports may be closed
  # explicitly with Ractor#close_outgoing/close_incoming and are closed implicitly
  # when a Ractor terminates.
  #
  #     r = Ractor.new { sleep(500) }
  #     r.close_outgoing
  #     r.take # Ractor::ClosedError
  #
  # ClosedError is a descendant of StopIteration, so the closing of the ractor
  # will break the loops without propagating the error:
  #
  #     r = Ractor.new do
  #       loop do
  #         msg = receive # raises ClosedError and loop traps it
  #         puts "Received: #{msg}"
  #       end
  #       puts "loop exited"
  #     end
  #
  #     3.times{|i| r << i}
  #     r.close_incoming
  #     r.take
  #     puts "Continue successfully"
  #
  # This will print:
  #
  #     Received: 0
  #     Received: 1
  #     Received: 2
  #     loop exited
  #     Continue successfully
  #
  class ClosedError < StopIteration
  end

  class Error < RuntimeError
  end

  class IsolationError < Ractor::Error
  end

  # <!-- rdoc-file=ractor.c -->
  # Raised on an attempt to access an object which was moved in Ractor#send or
  # Ractor.yield.
  #
  #     r = Ractor.new { sleep }
  #
  #     ary = [1, 2, 3]
  #     r.send(ary, move: true)
  #     ary.inspect
  #     # Ractor::MovedError (can not send any methods to a moved object)
  #
  class MovedError < Ractor::Error
  end

  # <!-- rdoc-file=ractor.c -->
  # A special object which replaces any value that was moved to another ractor in
  # Ractor#send or Ractor.yield. Any attempt to access the object results in
  # Ractor::MovedError.
  #
  #     r = Ractor.new { receive }
  #
  #     ary = [1, 2, 3]
  #     r.send(ary, move: true)
  #     p Ractor::MovedObject === ary
  #     # => true
  #     ary.inspect
  #     # Ractor::MovedError (can not send any methods to a moved object)
  #
  class MovedObject < BasicObject
    public

    # <!--
    #   rdoc-file=ractor.c
    #   - !(*args)
    # -->
    #
    def !: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - !=(*args)
    # -->
    #
    def !=: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - ==(*args)
    # -->
    #
    def ==: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - __id__(*args)
    # -->
    #
    def __id__: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - __send__(*args)
    # -->
    #
    def __send__: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - equal?(*args)
    # -->
    #
    def equal?: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - instance_eval(*args)
    # -->
    #
    def instance_eval: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - instance_exec(*args)
    # -->
    #
    def instance_exec: (*untyped) -> untyped

    # <!--
    #   rdoc-file=ractor.c
    #   - method_missing(*args)
    # -->
    #
    def method_missing: (*untyped) -> untyped
  end

  # <!-- rdoc-file=ractor.c -->
  # Raised on attempt to Ractor#take if there was an uncaught exception in the
  # Ractor. Its `cause` will contain the original exception, and `ractor` is the
  # original ractor it was raised in.
  #
  #     r = Ractor.new { raise "Something weird happened" }
  #
  #     begin
  #       r.take
  #     rescue => e
  #       p e             # => #<Ractor::RemoteError: thrown by remote Ractor.>
  #       p e.ractor == r # => true
  #       p e.cause       # => #<RuntimeError: Something weird happened>
  #     end
  #
  class RemoteError < Ractor::Error
    public

    def ractor: () -> Ractor
  end

  class UnsafeError < Ractor::Error
  end
end
