テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(nim-fuse report) nim-fuseにおけるreply関数の制限方法

fuseの言語バインディングを作る時, ただ作ったのでは良くない. fuseを知り尽くし, 言語に合ったものを作らなければやる意味がない. これは仕事ではなく, 自分の力を誇示するためだけに行うOSS開発なのだから. 世界のどこかでNimを使った開発が始まる時(きっとまだ企業レベルでの実績はほぼゼロである), Nimが書ける人ということでリストに上がりたい. 特に, NimによるOS開発には興味がある. Nim+OSならば, 世界レベルでもリストに上がる自信はある.

fuseのlowlevel interfaceでは, それぞれのopにreply関数が強制される. どういうことだろうか?例えば, mkdir opは, reply_entryかreply_errによってカーネルにackするように決められている.

        /**
         * Create a directory
         *
         * Valid replies:
         *   fuse_reply_entry
         *   fuse_reply_err
         *
         * @param req request handle
         * @param parent inode number of the parent directory
         * @param name to create
         * @param mode with which to create the new file
         */
        void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
                       mode_t mode);

技術的負債生産用言語たるC言語では, これを静的に制限することが出来ない. しかしその自由さは, 明らかに悪い. 実際, 何が返すことが許されているのかいちいち調べながらやるのは面倒だし, 何かバグった時に, reply関数が違うのでは?と疑わなければいけないこと自体が愚かである. reply関数を制限出来ると良い.

その方式の一つとして, NimのEffect Systemを使う方法を今朝思いついたのだが, この案は棄却することとした. 私がやりたいのは, 「呼ぶ側が何を呼べるか制限する」ことであり, 「あるreply関数がどの静的なコンテキストから呼べるか制限する」ことではないからである. 局所的・現実的にはこれらは同値となるだろうが, このような論理の綻びをinterfaceに持ち上げるのはよろしくない.

私がとった戦略は以下のコードを見ればわかる. templateを使った解である. このようにMkdir reply型を作り, これをreply引数としてopに渡す. この, reply引数を渡す設計自体はrust-fuseを参考にしたものであるが, 残念ながら, rust-fuseは粒度が粗い.

defWrapper(Mkdir)
defEntry(Mkdir)
defErr(Mkdir)

rust-fuseでは, reply型を個別に作っていないため, 個別の規約を吸収出来ないのである. 現実的に, read opがfuse_reply_iovをサポート出来なくなっているため, 性能上問題が出る可能性が高い. このように, フレームワークに制限があるとユーザから見ると嫌なものである. 一方で私のnim-fuseはtemplateを使って個別にreply型を生成しているため, 理論上のベストである. この設計は美しいため, 他のプログラマは感心するだろう. ただ動くものと美しいものは, 大差なのである. 特に, Nimのような新興言語のバインディングを使いたいと, 一般のfuseアプリケーションプログラマが思うのようになるには, フレームワークが圧倒的に良くなければいけない.

    /// Create file node
    /// Create a regular file, character device, block device, fifo or socket node.
    fn mknod (&mut self, _req: &Request, _parent: u64, _name: &PosixPath, _mode: u32, _rdev: u32, reply: ReplyEntry) {
        reply.error(ENOSYS);
    }

    /// Create a directory
    fn mkdir (&mut self, _req: &Request, _parent: u64, _name: &PosixPath, _mode: u32, reply: ReplyEntry) {
        reply.error(ENOSYS);
    }

rust-fuseの欠点を挙げているが, disっているわけではない. むしろ, この開発者をかなり尊敬している. rust-fuseがなければ, 私がこの設計を思いつけたかは怪しい. 少なくともこの短期間でたどり着くことはまず無理だっただろう. お互いにいいところを取り入れつつ, 新興言語Nim/RustがGoを駆逐する未来に向かっていければ良いと思う. やっぱり本当にGoが嫌いだ.

nim-fuseは, 1月中に完成することはまずない. 2月半ばを目標にしている. バレンタイン代わりに, 世界中に愛を届けたい.