class Backports::Ractor

def [](key)

def [](key)
  Ractor.current.ractor_locals[key]
end

def []=(key, value)

def []=(key, value)
  Ractor.current.ractor_locals[key] = value
end

def close_incoming

def close_incoming
  r = ractor_incoming_queue.closed?
  ractor_incoming_queue.close
  r
end

def close_outgoing

def close_outgoing
  r = ractor_outgoing_queue.closed?
  ractor_outgoing_queue.close
  r
end

def count

def count
  ::ObjectSpace.each_object(Ractor).count(&:ractor_live?)
end

def current

def current
  ::Thread.current.thread_variable_get(:backports_ractor) ||
    ::Thread.current.thread_variable_set(:backports_ractor, ractor_find_current)
end

def initialize(*args, &block)

def initialize(*args, &block)
  @ractor_incoming_queue = IncomingQueue.new
  @ractor_outgoing_queue = OutgoingQueue.new
  raise ::ArgumentError, 'must be called with a block' unless block
  kw = args.last
  if kw.is_a?(::Hash) && kw.size == 1 && kw.key?(:name)
    args.pop
    name = kw[:name]
  end
  @ractor_name = name && Backports.coerce_to_str(name)
  @id = Ractor.ractor_next_id
  if Ractor.main == nil # then initializing main Ractor
    @ractor_thread = ::Thread.current
    @ractor_origin = nil
    @ractor_thread.thread_variable_set(:backports_ractor, self)
  else
    @ractor_origin = caller(1, 1).first.split(':in `').first
    args.map! { |a| Ractor.ractor_isolate(a, false) }
    ractor_thread_start(args, block)
  end
end

def inspect

def inspect
  state = RACTOR_STATE[@ractor_thread ? @ractor_thread.status : 'run']
  info = [
    "Ractor:##{@id}",
    name,
    @ractor_origin,
    state,
  ].compact.join(' ')
  "#<#{info}>"
end

def make_shareable(obj)

def make_shareable(obj)
  return obj if ractor_check_shareability?(obj, true)
  raise Ractor::Error, '#freeze does not freeze object correctly'
end

def name

def name
  @ractor_name
end

def ractor_check_shareability?(obj, freeze_all)

def ractor_check_shareability?(obj, freeze_all)
_shareable_self?(obj, freeze_all) do
ted = {}
rn false unless ractor_shareable_parts?(obj, freeze_all, visited)
or_mark_set_shareable(visited)

def ractor_find_current

def ractor_find_current
ThreadGroups[Thread.current.group]

def ractor_init

def ractor_init
r_shareable = ::ObjectSpace::WeakMap.new
= Ractor.new { nil }
ThreadGroups[::ThreadGroup::Default] = @main

def ractor_isolate(val, move = false)

Other tags:
    Api: - private
def ractor_isolate(val, move = false)
  return val if move
  Cloner.deep_clone(val)
end

def ractor_live?

Other tags:
    Api: - private
def ractor_live?
  !defined?(@ractor_thread) || # May happen if `count` is called from another thread before `initialize` has completed
    @ractor_thread.status
end

def ractor_locals

Other tags:
    Api: - private
def ractor_locals
  @ractor_locals ||= {}.compare_by_identity
end

def ractor_mark_set_shareable(visited)

def ractor_mark_set_shareable(visited)
  visited.each do |key|
    @ractor_shareable[key] = Ractor
  end
end

def ractor_next_id

Other tags:
    Api: - private
def ractor_next_id
  @id ||= 0
  @id += 1
end

def ractor_reset

Other tags:
    Api: - private
def ractor_reset
  ::ObjectSpace.each_object(Ractor).each do |r|
    next if r == Ractor.current
    next unless (th = r.ractor_thread)
    th.kill
    th.join
  end
  Ractor.current.ractor_incoming_queue.clear
end

def ractor_shareable_by_nature?(obj, freeze_all)

def ractor_shareable_by_nature?(obj, freeze_all)
bj
:Module, Ractor

:Regexp, ::Range, ::Numeric
eze_all # Assume that these are literals that would have been frozen in 3.0
        # unless we're making them shareable, in which case we might as well
        # freeze them for real.
:Symbol, false, true, nil # Were only frozen in Ruby 2.3+

e

def ractor_shareable_parts?(obj, freeze_all, visited)

def ractor_shareable_parts?(obj, freeze_all, visited)
 true if visited.key?(obj)
d[obj] = true
_traverse(obj) do |part|
rn false unless ractor_shareable_self?(part, freeze_all) do
ctor_shareable_parts?(part, freeze_all, visited)

def ractor_shareable_self?(obj, freeze_all)

yield if shareability can't be determined without looking at its parts
def ractor_shareable_self?(obj, freeze_all)
  return true if @ractor_shareable.key?(obj)
  return true if ractor_shareable_by_nature?(obj, freeze_all)
  if obj.frozen? || (freeze_all && obj.freeze)
    yield
  else
    false
  end
end

def ractor_thread_start(args, block)

def ractor_thread_start(args, block)
ad.new do
tor_thread = ::Thread.current
tor_thread_group = ::ThreadGroup.new
orThreadGroups[@ractor_thread_group] = self
tor_thread_group.add(@ractor_thread)
read.current.thread_variable_set(:backports_ractor, self)
lt = nil
n
sult = instance_exec(*args, &block)
ue ::Exception => err # rubocop:disable Lint/RescueException
gin
raise RemoteError, "thrown by remote Ractor: #{err.message}"
scue RemoteError => e # Hack to create exception with `cause`
result = OutgoingQueue::WrappedException.new(e)
d
re
ctor_thread_terminate(result)

def ractor_thread_terminate(result)

def ractor_thread_terminate(result)
or_outgoing_queue.push(result, ack: false) unless ractor_outgoing_queue.closed?
 ::ClosedQueueError
rn # ignore
_incoming_queue.close
_outgoing_queue.close(:soft)
: synchronize?
r_thread_group.list.each do |thread|
ad.kill unless thread == Thread.current

def ractor_traverse(obj, &block)

def ractor_traverse(obj, &block)
bj
:Hash
 obj.default
d obj.default_proc
each do |key, value|
eld key
eld value
:Range
d obj.begin
d obj.end
:Array, ::Struct
each(&block)
:Complex
d obj.real
d obj.imaginary
:Rational
d obj.numerator
d obj.denominator
stance_variables.each do |var|
d obj.instance_variable_get(var)

def receive

def receive
_incoming_queue.pop

def receive

def receive
  current.__send__(:receive)
end

def receive_if(&block)

def receive_if(&block)
::ArgumentError, 'no block given' unless block
_incoming_queue.pop(&block)

def receive_if(&block)

def receive_if(&block)
  current.__send__(:receive_if, &block)
end

def select(*ractors, yield_value: not_given = true, move: false)

def select(*ractors, yield_value: not_given = true, move: false)
  cur = Ractor.current
  queues = ractors.map do |r|
    r == cur ? r.ractor_incoming_queue : r.ractor_outgoing_queue
  end
  if !not_given
    out = current.ractor_outgoing_queue
    yield_value = ractor_isolate(yield_value, move)
  elsif ractors.empty?
    raise ::ArgumentError, 'specify at least one ractor or `yield_value`'
  end
  while true # rubocop:disable Style/InfiniteLoop
              # Don't `loop`, in case of `ClosedError` (not that there should be any)
    queues.each_with_index do |q, i|
      q.pop_non_blocking do |val|
        r = ractors[i]
        return [r == cur ? :receive : r, val]
      end
    end
    if out && out.num_waiting > 0
      # Not quite atomic...
      out.push(yield_value, ack: true)
      return [:yield, nil]
    end
    sleep(0.001)
  end
end

def send(obj, move: false)

def send(obj, move: false)
  ractor_incoming_queue << Ractor.ractor_isolate(obj, move)
  self
rescue ::ClosedQueueError
  raise ClosedError, 'The incoming-port is already closed'
end

def shareable?(obj)

def shareable?(obj)
  ractor_check_shareability?(obj, false)
end

def take

def take
  ractor_outgoing_queue.pop(ack: true)
end

def yield(value, move: false)

def yield(value, move: false)
  value = ractor_isolate(value, move)
  current.ractor_outgoing_queue.push(value, ack: true)
rescue ::ClosedQueueError
  raise ClosedError, 'The outgoing-port is already closed'
end