class Opal::BuilderScheduler::Prefork

def fork_count

By default we use 3/4 of CPU threads detected.
def fork_count
  ENV['OPAL_PREFORK_THREADS']&.to_i || (Etc.nprocessors * 3 / 4.0).ceil
end

def fork_entrypoint(io)

def fork_entrypoint(io)
  # Ensure we can work with our forks async...
  Fiber.set_scheduler(nil) if Fiber.respond_to? :set_scheduler
  @in_fork = io
  until io.eof?
    $0 = 'opal/builder: idle'
    type, *args = *io.recv
    case type
    when :compile
      rel_path, req, autoloads, options = *args
      $0 = "opal/builder: #{req}"
      begin
        asset = builder.process_require_threadsafely(req, autoloads, options)
        io.send(:new_asset, asset)
      rescue Builder::MissingRequire => error
        io.send(:missing_require_exception, rel_path, error)
      end
    when :close
      io.goodbye
      break
    end
  end
rescue Errno::EPIPE
  exit!
end

def prefork

def prefork
  @forks = ForkSet.new(fork_count, &method(:fork_entrypoint))
end

def prefork_reactor(rel_path, requires, autoloads, options)

def prefork_reactor(rel_path, requires, autoloads, options)
  prefork
  processed = []
  first = rel_path
  queue = requires.map { |i| [rel_path, i, autoloads, options] }
  awaiting = 0
  built = 0
  should_log = $stderr.tty? && !ENV['OPAL_DISABLE_PREFORK_LOGS']
  $stderr.print "\r\e[K" if should_log
  loop do
    events, idles = @forks.get_events(queue.length)
    idles.each do |io|
      break if queue.empty?
      rel_path, req, autoloads, options = *queue.shift
      next if builder.already_processed.include?(req)
      awaiting += 1
      builder.already_processed << req
      io.send(:compile, rel_path, req, autoloads, options)
    end
    events.each do |io, type, *args|
      case type
      when :new_requires
        rel_path, requires, autoloads, options = *args
        requires.each do |i|
          queue << [rel_path, i, autoloads, options]
        end
      when :new_asset
        asset, = *args
        if !asset
          # Do nothing, we received a nil which is expected.
        else
          processed << asset
        end
        built += 1
        awaiting -= 1
      when :missing_require_exception
        rel_path, error = *args
        raise error, "A file required by #{rel_path.inspect} wasn't found.\n#{error.message}", error.backtrace
      when :exception
        error, = *args
        raise error
      when :close
        io.goodbye
      end
    end
    if should_log
      percent = (100.0 * built / (awaiting + built)).round(1)
      str = format("[opal/builder] Building %<first>s... (%<percent>4.3g%%)\r", first: first, percent: percent)
      $stderr.print str
    end
    break if awaiting == 0 && queue.empty?
  end
  processed
ensure
  $stderr.print "\r\e[K\r" if should_log
  @forks.close
  @forks.wait
end

def process_requires(rel_path, requires, autoloads, options)

We hook into the process_requires method
def process_requires(rel_path, requires, autoloads, options)
  return if requires.empty?
  if @in_fork
    io = @in_fork
    io.send(:new_requires, rel_path, requires, autoloads, options)
  else
    processed = prefork_reactor(rel_path, requires, autoloads, options)
    processed = OrderCorrector.correct_order(processed, requires, builder)
    builder.processed.append(*processed)
  end
end