Songmu/timeoutからコマンドの停止処理部分を理解しつつ切り出してk1LoW/execを作っている #gocon

Go Conference'19 Summer in Fukuoka、私の中でもう少し続いています。

exec.CommandContext は "孫プロセスがあった場合にそれが止まらない"

songmuさんの発表は聞きたいなと思っていたのですが、40分間これでもかと知見が込められた発表でした。ツール作るマンとしてはいろいろ心に留めておきたいことばかりでした。

その中でも「コマンドを停止する」の章は、実はスライドレベルでいうと2回目なのですが(ヒント: Go Conference 2019 Spring)、やはり直接聞くと「ごめんなさいごめんなさい」という気持ちになりました。

というのも、私はよく以下のように exec.CommandContext を書くのですが、

cmd := exec.CommandContext(ctx, "bash", "-c", commandStr)

ctxのキャンセルに任せてしてしまって、孫プロセスになるcommandStrを放置している。。。

GoConでコマンドの停止処理について、スライド資料とともにコードレベルでしっかりと解説してもらったので、これはしっかりと書いていこうと思いました。

とはいえ、いろんなところに exec.CommandContext をばらまいてしまっている。。。

k1LoW/exec

というわけで、勉強と実益を兼ねて、os/execと同じ書き方ができつつも、孫プロセスを見捨てないk1LoW/execを作りはじめています。

github.com

使い方はos/execと全く同じです。os/execにinterfeceがあるわけではありませんが、os/execと同じメソッドは全て用意しているので、そのまま切り替えて使えます。

import (
    // "os/exec"
    "github.com/k1LoW/exec"
)

また、k1LoW/exec.Command の返り値も *os/exec.Cmd のままです。

ただ、さきほどと同じように書いても、

cmd := exec.CommandContext(ctx, "bash", "-c", commandStr)

ctxがキャンセルした時には孫プロセスのcommandStrにもちゃんとシグナルが渡ります。

つまり os/execgithub.com/k1LoW/execに差し替えれば解決!! というのを目指しています。

import "github.com/Songmu/timeout"

k1LoW/execのコマンド停止処理部分は、発表で学んだ内容 = Songmu/timeoutのコマンド停止処理部分が ほぼそのまま です。実質Songmu/timeoutをライブラリとしてimportしている感じです。

それでも書いて良かった点は、テストコードを自分なりに書いてos/execとの違いを確認したことです。

os/exec.CommandContext ではFAILになるのに、k1LoW/exec.CommandContext ではSUCCESSになるというのは「なるほどー!!」となる瞬間でした。これは発表を聞いただけの理解の時とは違いました。具体的に手を動かして良かったです。

これから

k1LoW/execでは、あと以下の2つを実現したいと思っています。

Windowsサポート

Songmu/timeoutWindowsもしっかりサポートしており、かつテストもとてもわかりやすく書かれています。これを参考にWindowsのコマンド停止処理までしっかりと移植しつつ、WindowsのテストやCIの書き方を学んでいきます。

KillAfter/KillAfterCancelのサポート

KillAfter/KillAfterCancelSongmu/timeoutにある実装です。 コマンドの停止処理のお作法であるSIGTERM -> SIGKILLの流れ を、出来るだけ今のos/execなインターフェースを崩さずに実装しようと思っています。こちらは、あとは書くだけかなと思っています。

というわけで

songmuさん、知見のつまった発表ありがとうございました。ぜひ、またお会いしましたらよろしくお願いします。

残りの実装も書いていきます。