Windows+Rubyで外部プロセスを立ち上げる時の注意

Windows上で、Rubyから外部プロセス*1を起動〜終了監視する方法のメモ。


Windowsでプロセス起動〜終了制御を行うならwin32-processが便利。

しかしこれはノンブロッキングモードをサポートしておらず、終了監視メソッドwaitpid()はスレッドをブロックしてしまう。例えばDRbの内部で使うと、起動したプロセスが終了するまでDRb全体が応答しなくなる。それは困る。


そこで、Win32APIを直接呼び出してプロセスを制御することで回避。

require 'rubygems'
require 'win32/process'
require 'windows/synchronize'
require 'windows/process'
require 'windows/handle'

include Windows::Synchronize
include Windows::Process
include Windows::Handle

#外部プロセス起動・終了待ちスレッドの作成
t = Thread.new do
  #init.sql内SQL文での長大な処理を想定。(テスト時はnotepad.exe等で代用)
  pi = Process.create("app_name" => "sqlite3.exe -init init.sql test.db .q")
  #プロセス監視をWin32APIで行う
  until WaitForSingleObject(pi.process_handle, 0) == WAIT_OBJECT_0
    puts "waiting:(#{Time.now})"
    sleep 1
  end
  #プロセスハンドルを閉じる
  CloseHandle(pi.process_handle)
end
t.join

これならばWIN32APIによる制御となるので、スレッドはブロックされない。
win32-processのwaitpidもWIN32APIで待ちをしているのだけれど、プロセス終了監視モードを使っているためにスレッドがブロックされてしまうようである。標準のwaitpidと引数互換性が保てないので、あえてそうしたのかな?
とりあえずこの方法で回避できるのでよしとする。

*1:今回はsqlite3.exeを使おうとして嵌った