Deno.exit() の修正 watcherも含めて
上記issueにて「Deno.exit()
でDenoプロセスを終了させると、watcherも死んでしまう」という問題が報告されている。つまり、
$ deno run --unstable --watch foo.ts
Deno.exit(1);
とすると、watcherも死んでしまうためファイルの変更を監視できなくなる、ということ。
期待する挙動としては、Deno.exit()
でプロセスを終了させたとしても、watcherは生き残って、ファイルの変更を監視し続ける、というもの。
これを修正するために行った調査結果などをまとめていきたい
まず、伝家の宝刀†プリントデバッグ†を使いながら、状況を探る。
cli/file_watcher.rs
にいろいろ プリントを仕込んでみると、
async fn error_handler(watch_future: FileWatcherFuture<Result<(), AnyError>>) {
eprintln!("44444444444");
let result = watch_future.await;
dbg!(&result);
if let Err(err) = result {
let msg = format!("{}: {}", colors::red_bold("error"), err.to_string(),);
eprintln!("{}", msg);
}
}
ここが怪しいということがわかった。具体的には、44444444444
は出力されるものの、&result
は出力されない。つまり watch_future.await
でプロセスが強制終了している。
調査する前の予想としては、Deno.exit()
をしたとしたら Rust 側では Err
型として取り扱われると思っていたのだが、どうやらそうではない模様。意外と大変な修正になるのかもしれないという予感がし始める。
TS/JS で Deno.exit()
を呼んだときにどういう処理が走るのか、をもとに追ってみることにする。
まず、Deno.exit()
の処理内容は、JSサイドでは以下のように書かれている
function exit(code = 0) {
core.jsonOpSync("op_exit", { code });
throw new Error("Code not reachable");
}
op_exit
という op をRust側に送っていることがわかるので、Rust側の処理を探すと↓が見つかる
fn op_exit(
_state: &mut OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let args: Exit = serde_json::from_value(args)?;
std::process::exit(args.code)
}
はは〜ん、std::process::exit(args.code)
があるので問答無用でプロセスが終了していたというわけだ。
Deno コアチームメンバーの Bartek に「この issue も関連するかも」と教えてもらったのが、以下である
要約すると「Deno.exit
したときとか SIGINT
が来たときに window.onunload
で設定したイベントハンドラが動かない!」ということ。なるほど、確かに Deno.exit()
周りの実装を変える必要があるという点で関連している気がする。
ちょっと横道に逸れるかもしれないが、SIGINT
などのシグナルを Rust でうまいこと処理する方法は
が参考になるかも?
さて、Deno.exit
をしてもwatcherが死なないようにするためには、watcherを親プロセス、スクリプトをrunするのを子プロセスとして起動してあげないといけないっぽいことがわかってきた。(今は同一プロセスでやっている)
-
--watch
フラグ付きで起動した場合には、--watch
を取り除いたコマンドを使って 子プロセスを起動する - Rustの標準ライブラリにある std::process::Child などは同期的なAPIなので、Denoの実装と相性が悪い。tokioの
process
モジュール を使うのが良さそう - tokioのprocessモジュールを使って子プロセスを起動し終了をawait, 同時に file watcher(これは無限streamになっていて、監視対象ファイルに変更があると
Ready
が返ってくる)をawait する。この2つを tokio::select で待ち受ける。
みたいな感じになりそう