Class: Chocomint::Console::WorkerProcess

Inherits:
Object
  • Object
show all
Defined in:
lib/chocomint/console/worker_process.rb

Overview

コンソールワーカーを「新規コンソール付き」で spawn し、名前付きパイプで 双方向にやり取りするための橋渡し。

なぜ名前付きパイプか (重要): ConPTY はコンソール付きプロセスからしか 正しく動かない (chocomint-conpty メモの制約)。CREATE_NEW_CONSOLE で新規 コンソールを与える必要があるが、STARTF_USESTDHANDLES で stdout をパイプへ リダイレクトすると新規コンソールとの関連付けが壊れ、ConPTY 出力が 16 バイトの 初期シーケンスで止まる (実測で確認)。そこで stdio は触らず、制御通信は 独立した名前付きパイプで行う。ワーカーはコンソールを完全に保持できる。

Defined Under Namespace

Modules: Native Classes: Error

Instance Method Summary collapse

Constructor Details

#initialize(command:, cols:, rows:, cwd:, ruby_exe: nil, worker_script: nil) ⇒ WorkerProcess

Returns a new instance of WorkerProcess.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/chocomint/console/worker_process.rb', line 69

def initialize(command:, cols:, rows:, cwd:, ruby_exe: nil, worker_script: nil)
  @command = command
  @cols = cols
  @rows = rows
  @cwd = cwd
  @ruby_exe = ruby_exe || default_ruby_exe
  @worker_script = worker_script || default_worker_script
  # 双方向を 1 本のパイプで共有すると read/write が競合してブロックするため、
  # 方向ごとに 2 本張る (c2w: 親→worker, w2c: worker→親)。
  base = "\\\\.\\pipe\\chocomint-console-#{SecureRandom.hex(8)}"
  @c2w_name = "#{base}-c2w"
  @w2c_name = "#{base}-w2c"
  @open = false
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


124
125
126
127
128
129
130
131
# File 'lib/chocomint/console/worker_process.rb', line 124

def alive?
  return false unless @proc_handle

  code = FFI::MemoryPointer.new(:uint32)
  return false if Native.GetExitCodeProcess(@proc_handle, code).zero?

  code.read(:uint32) == Native::STILL_ACTIVE
end

#closeObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/chocomint/console/worker_process.rb', line 133

def close
  return unless @open

  Native.TerminateProcess(@proc_handle, 0) if alive?
  [@c2w, @w2c].each do |h|
    next unless h

    Native.DisconnectNamedPipe(h)
    Native.CloseHandle(h)
  end
  Native.CloseHandle(@proc_handle) if @proc_handle
  @open = false
  nil
end

#open?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/chocomint/console/worker_process.rb', line 84

def open?
  @open
end

#read_messageObject

ワーカーからの JSON 行を 1 つ読む。無ければ nil (ノンブロッキング)。



115
116
117
118
119
120
121
122
# File 'lib/chocomint/console/worker_process.rb', line 115

def read_message
  line = read_line
  return nil if line.nil? || line.empty?

  JSON.parse(line)
rescue JSON::ParserError
  nil
end

#send_input(bytes) ⇒ Object



106
107
108
# File 'lib/chocomint/console/worker_process.rb', line 106

def send_input(bytes)
  send_message("type" => "input", "data" => Base64.strict_encode64(bytes.b))
end

#send_message(hash) ⇒ Object



102
103
104
# File 'lib/chocomint/console/worker_process.rb', line 102

def send_message(hash)
  write_bytes("#{JSON.generate(hash)}\n".b)
end

#send_resize(cols, rows) ⇒ Object



110
111
112
# File 'lib/chocomint/console/worker_process.rb', line 110

def send_resize(cols, rows)
  send_message("type" => "resize", "cols" => cols, "rows" => rows)
end

#startObject

名前付きパイプを 2 本作り、ワーカーを新規コンソール付きで spawn して接続を待つ。

Raises:



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/chocomint/console/worker_process.rb', line 89

def start
  raise Error, "already started" if @open

  @c2w = create_named_pipe(@c2w_name)
  @w2c = create_named_pipe(@w2c_name)
  spawn_worker
  # ワーカーが両パイプに接続してくるのを待つ (順序はワーカーの open と一致させる)。
  Native.ConnectNamedPipe(@c2w, nil)
  Native.ConnectNamedPipe(@w2c, nil)
  @open = true
  self
end