Steepコードリーディング(3日目)

# Steepコードリーディング(3日目)

次回の続き、`Steep::Drivers::Check#run` の `Server::WorkerProcess.start_typecheck_workers` を追っていく。

## `Server::WorkerProcess`

https://github.com/soutaro/steep/blob/290ab9a3d023f1ed8011fb720c420c611e5473b9/lib/steep/server/worker_process.rb

### `Server::WorkerProcess#.start_typecheck_workers`

`Etc.nprocessors`は有効なCPUコア数を返す。コア数に応じて並列処理してるという感じかな。

```rb
def self.start_typecheck_workers(steepfile:, args:, steep_command:, count: [Etc.nprocessors - 1, 1].max || raise, delay_shutdown: false)
  count.times.map do |i|
    start_worker(
      :typecheck,
      name: "typecheck@#{i}",
      steepfile: steepfile,
      steep_command: steep_command,
      index: [count, i],
      patterns: args,
      delay_shutdown: delay_shutdown,
    )
  end
end
```

### `Server::WorkerProcess#.start_worker`

ターミナルから `$ bundle exec steep check` を実行してる場合、`steep_command`には`"steep"`があるはずなので、`spawn_worker`が実行される。

```rb
def self.start_worker(type, name:, steepfile:, steep_command:, index: nil, delay_shutdown: false, patterns: [])
  begin
    unless steep_command
      fork_worker(
        type,
        name: name,
        steepfile: steepfile,
        index: index,
        delay_shutdown: delay_shutdown,
        patterns: patterns
      )
    else
      # Use `#spawn_worker`
      raise NotImplementedError
    end
  rescue NotImplementedError
    spawn_worker(
      type,
      name: name,
      steepfile: steepfile,
      steep_command: steep_command || "steep",
      index: index,
      delay_shutdown: delay_shutdown,
      patterns: patterns
    )
  end
end
```

### `Server::WorkerProcess#.spawn_worker`

`type`引数には `:typecheck`が渡っているため、`command`は`["steep", "worker", "--typecheck", "--name=typecheck@1", "--steepfile=Steepfile", "--log-level=error", "--max-index=1", "--index=1", "--delay-shutdown"]`こんな感じになりそう。

自分の環境はWindowsではないので `stdin, stdout, thread = Open3.popen2(*command, pgroup: true)`となりそう。`Open3.popen2`とは[これ](https://docs.ruby-lang.org/ja/latest/method/Open3/m/popen2.html)。

```rb
def self.spawn_worker(type, name:, steepfile:, steep_command:, index:, delay_shutdown:, patterns:)
  args = ["--name=#{name}"]
  args << "--steepfile=#{steepfile}" if steepfile
  args << (%w(debug info warn error fatal unknown)[Steep.logger.level].yield_self {|log_level| "--log-level=#{log_level}" })

  if Steep.log_output.is_a?(String)
    args << "--log-output=#{Steep.log_output}"
  end

  if (max, this = index)
    args << "--max-index=#{max}"
    args << "--index=#{this}"
  end

  if delay_shutdown
    args << "--delay-shutdown"
  end

  command = case type
            when :interaction
              [steep_command, "worker", "--interaction", *args, *patterns]
            when :typecheck
              [steep_command, "worker", "--typecheck", *args, *patterns]
            else
              raise "Unknown type: #{type}"
            end

  stdin, stdout, thread = if Gem.win_platform?
                            __skip__ = Open3.popen2(*command, new_pgroup: true)
                          else
                            __skip__ = Open3.popen2(*command, pgroup: true)
                          end
  stderr = nil

  writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
  reader = LanguageServer::Protocol::Transport::Io::Reader.new(stdout)

  new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name, index: index&.[](1))
end
```