生涯未熟

プログラミングをちょこちょこと。

fsnotifyは何故Vimで動かないのか?

という掲題ですが、まさにハマったことなのでメモ代わりに記事として書いておきます。

一体何が?

Goでファイル監視をしようと思いライブラリを探した結果、fsnotifyというライブラリを発見しました。

github.com

「Star数も多いし、こりゃいいや。使っちゃれ。」と思い、使ってみたところ何故かファイル変更が監視できていませんでした。
ここから色々と探る旅が始まりました。

尚、今回環境としてはOS X + Gogland + IdeaVimになります。

まずはライブラリを読んでみる

fsnotifyは以下のように使っていました。

で、このコードからライブラリの中身を探索することに。

最初に使っているfsnotifyの要素としては NewWatcher です。
試しにこの処理を追いかけてみましょう。

ふむふむ、ここで出てきている kqueue というのが謎だったので、調べてみる。

kqueue - Wikipedia

なーるほど、BSD系のファイル監視ツールなのですね。
OS XFreeBSDをベースとしているので、勿論このkqueueが使えます。
ということは、このfsnotifyはOSによってファイル監視ツールを使い分けていて、今回の場合 kqueue を使っているということですね。

さて、ここまで来たら次に気になるのは「kqueueでファイル監視してみて再現するのか?」ということでした。

kqueueを使ってみる

では、kqueueを使ってファイル監視してみましょう。

Goだとこんな感じで、kqueueを使ったファイル監視が実現できます。

これを実行してみた結果・・・ダメでした(´・ω・`)

今回の件はfsnotifyの実装が悪いということではないことが分かりました。
ということは、残りの可能性としてはVimが悪いということか・・・?

Vim以外のエディタで試してみる

fsnotifyでファイル監視し、ファイルを別のエディタで編集してみました。(使用エディタはMacのテキストエディット)
結論から言うと、ちゃんとファイル監視出来ていました

Vim、お前だったのか・・・

では、何故Vimだとダメだったのでしょう?
今度はそれを探りに行きます。

kqueue ✕ Vim = ダメ?

まず手始めにfsnotifyのIssueを探し回りました。
そこで発見したのは以下のようなIssueでした。

github.com

要約してみると、

質問者「Vim使った場合にのみ、ファイル編集後にイベントをキャッチしてくれなくなりました」
OSSオーナー「どうもVimのAtomic Saveの仕組みのせいだと思うよ」

なーるーほーどー。
このAtomic Saveってなんじゃらほい?と思い、これまた調べてみることに。

調べてみるとこのAtomic SaveはVimがファイル編集中に退避ファイル(.swpか~)を吐き出し、その後保存する機構のようです。
そういえば確かに、Vimでの編集中に.swpっての吐いてました。 保存までの流れをdtrussで詳しく追いかけた記事がありましたので、詳しく知りたい方は以下のご一読を。

Vim system calls when saving a file · GitHub

この仕組みがどうもkqueueによるファイル監視ができない原因の模様。

更なる調査

「んじゃどうすっかな・・・?」と調査を続けていると、Redditにこんな記事を見つけました。

www.reddit.com

これはまさに掲題の件を話しているスレッドになるのですが、その中で
「The best work around right now is to watch the directory containing test.txt instead.
(今のところ最善の方法は、代わりにtest.txtを含むディレクトリを見ることです)」

という文言を発見。

なるほど!監視対象をディレクトリにするのはまだ試していなかった!!と思い、すぐさま試してみることに。

おっ、これだとVimで編集しても無事にイベントをキャッチすることができました!
ただ、 ./test/*.go のようなワイルドカードを使った指定がfsnotifyでは出来なかったり、 ./test/test1 のような階層構造になったディレクトリを監視対象としてくだないようなので(これはkqueueの制限)色々と使い勝手が難しいですね:-(

OS Xのみになるのですが、FSEventsという仕組みがあってこちらを使うとディレクトリの階層構造を探索して、監視対象にはしてくれるみたいです。
一応fsnotify内で実装はされているようです、気になる方は見てみてください。

github.com

まとめ

  • fsnotifyを使う時はなるべくディレクトリを監視対象にしよう
  • kqueueには色々と制限があるのでMacのみに対応する場合はFSEventsを使うのもアリ

色々と探ってみた結果、得るものが多くて良かったです:-)

[追記] 「node.jsとかgulpとかでファイル単位での監視って出来たような・・・🤔」とか思って確かめてみましたがダメでした・・・
やはりkqueueの仕組みを使ってる模様。当たり前か。