@m_seki の

I like ruby tooから引っ越し

dRubyの最初のバージョンを投稿してから16年たったのを教えてもらいました。

dRubyの最初のバージョンを投稿してから16年たったのを教えてもらいました。久しぶりにdRuby前夜のことを思い出してみようと思います。
当然20世紀、ぎりぎり平成くらいの昔話。

dRubyによる分散・Webプログラミング

dRubyによる分散・Webプログラミング

The dRuby Book: Distributed and Parallel Computing with Ruby

The dRuby Book: Distributed and Parallel Computing with Ruby

Thread

最初はselectのないUNIXでシグナル、パイプやSysV IPCを使っていろいろ苦労してたんすよ。思い出せないくらいめんどくさかったし、たぶんいろいろ使い方間違ってた気がします。

その後select属の使える環境になってからは、全てをfdとすることで多元待ちできるようになりました。GUIX11)を持つときはXtとInputCallbackを使って、GUIのないときはselectを生で使って
そういうハンドラを実現しました。古き良きUNIX世代なので単機能の小さなプロセス同士(サーバ?)を連携させて全体のシステムを組み立てます。多くのプロセスはサーバでありクライアントでした。
プロセスの分割は、機能ごとというより、情報の生存期間に応じて分割されていた気もします(全てがそうではない)。まれに重い演算をするプロセスを避けておくために分割されることもありました。

GUIをもつアプリケーションにプロセス間通信をさせるプログラムもよく書きました。
GUIを持つ場合、持たない場合よりほんの少し難しさが増えます。画面の再描画を絶えず行う必要があるため、メインループ中で時間のかかる処理*1をできないのです。
できないというか、できるのだけど、そうすると画面の描画が遅れてしまいかっこわるい動きをするんです。
fdがreadable/writableになるとコールバックされますが、そこでブロッキングなread/writeをしてしまうのはまずいのです。
あくまでノンブロッキングモードでIOします。
メッセージが途中までしか読めてなくても、書けてなくても、一度処理をやめてメインループに戻らないといけません。
これは複数のプロセスと話するプログラムならどれも同じ制約ではあるけども、画面があるとバレるんです。スムーズなダメージング処理ができなくなるし、ユーザーの入力に遅れてしまう。

通信メッセージを受け取り、解釈して、演算をして、返信パケットを組み立てて返信する。
これ全部を一直線に記述したいのですが、ブロックする可能性のあるところでメインループへ戻る、長そうな処理のときはときどきメインループへ戻る必要があり難しいです。
継続が用意されている言語では簡潔に書けるけど、私の場合は、処理の中間結果をメモリに巧みに保存して*2、つぎのコールバックのタイミングで復元するようなプログラムを書いてました*3

まあ21世紀っぽくいうとNode風なやつです。

IOでブロックしそうなときや、長すぎる処理の途中で別のスレッドに処理を譲ってあげられる、そのくらいのスレッドがあれば本来の処理に集中できるのになー。当時(今も?)私が欲しかったのはコアを使い切るぜ*4!じゃなくて、こういったものが自然にかける程度のマルチスレッドでした。


まあ、それからずいぶん経ってRubyですよ。原さんのshttpsrvの実装を読みながらだんだん気づいてきた。これ欲しいやつそのまんま。なんで私が欲しいの知ってるんだよって感じ。
dRubyRubyのスレッドを1RMIに対応させて、メッセージの受信、メソッドの実行、結果の返信までを素直に一直線に書いてます。C10Kみたいな状況には耐えられないかもしれないけど、自分がこれまで経験してきたプロセス間通信の状況では十分に働きます。
# だいたい一人で10K相手にしなくたっていいじゃんな。

Marshal

これまでGUIとプロセス間通信をたくさんやってたので、メッセージの表現形式についても何種類か経験していました。いろんなものをXMLで表現したり、タグ付きの union、ビットの組み合わせによる型の表現、...めんどくさいんです。なにか新しいものを送るたびになにか発明しなきゃならない。処理について書くより、オブジェクトとメッセージの変換のコードばっかり書いてることが多くなってくる。新しいものが出るたびになにか書く方式はしんどい。

Marshalを選んだのは標準添付であることと、オブジェクトは基本的にマーシャル可能でマーシャル不可能なものの方を特別扱いするというポリシーが好みだったからです。ユーザー定義のオブジェクトもなにもしなくてもマーシャルできます。かっこいい。MarshalはRubyに依存しているため、メッセージとオブジェクトのインピーダンスミスマッチは起こりえません。

そうだ!私が欲しいのはWebServiceじゃなくて、Rubyオブジェクトをプロセスを超えて利用しあえるものだったんだ! WebServiceだったらたくさんあるもんね*5
世界中に対する一般化は無視。Ruby世界で一般化できればいいや。

この判断で一気にデザインが決まりました。MarshalにRubyに依存する。
なにを値渡しにしてなにを参照渡しにするのかについては、Marshalに任せよう*6Rubyのメソッド送信部分だけをネットワーク上の拡張しよう、
既存の分散システムの常識はあえて無視しよう。

なんか眠くなってきた。もうちょっとあるけど、あとでかく。かかないか。あした読み直して直そう。


投げ銭について

16歳なので、物販のディスカウントしてみました!(トリブン減らしただけ)
みんな記念に買ってね!いつでも買えるけど!

https://suzuri.jp/m_seki/
http://www.amazon.co.jp/gp/registry/wishlist/1R43BBPSPUEEE

*1:演算だけでなく通信待ちなども

*2:遺言を書いて

*3:さらにケチくさくmemmoveの回数を最小にしようとしたりもしてました

*4:コアを使い切るぜ!みたいなマルチスレッドのプログラムを書くならそういう種類の問題がないと合わないと思うんだよな。そういうのはまた別の話題

*5:当時はすくなかったけど

*6:しかも割とうまく働く