Croquet の特徴の一つに、複数のホストで同じ世界を共有する事があるのですが、突っ込んでやってるとかなりこの実行モデル特有の問題がある事が分かりました。まだ整理できていませんがメモします。まず環境について。
- 同じ環境を共有するホストが複数存在している。
- 全てのホストで共有されるオブジェクトがある。
- ホストごとにユーザは独立して操作するが、その結果は全てのホストに同期させたい。
例えば yes no の二つの選択肢からユーザが答えを選ぶと、それが表示される Smalltalk プログラムは以下のようになります。
[ reply := (SelectionMenu selections: #(yes no)) startUp. Transcript cr; show: reply. ] repeat
メニューを出して、答えを表示して、それを無限回繰り返すという大変シンプルなプログラムになります。しかし、これを P2P で共有される環境でやろうとすると大変面倒な事になります。擬似コードを書きます。
makeMenu menu := (SelectionMenu meta selections: #(yes no)) meta start. self meta startScript: #handleMenu: with: menu handleMenu: menu reply := menu getReply. reply ifNotNil: [Transcript meta cr; show: reply. self makeMenu]
ここで、meta というのは、「全部のホストに伝える」という意味だと思ってください。何故だか Croquet ではそういう意味に使っています。ここ最悪な事は、無限ループを明示できない事と、次の処理を指定するのに startScript: を使う事です。処理の進み方はこうなっています。
- あるホストが、全てのホストに、メニューを立ち上げるよう伝える。
- 全てのホストでメニューの答えを待ち受ける。
- 答えを得たホストが、全てのホストに、答えを表示するよう伝える。
- 答えを得たホストが全てのホストに、メニューを立ち上げるよう伝える(最初に戻る)。
つまり、処理の流れは、ユーザが答えたホストを縫い針の如くさまよって行きます。遠目で見れば全体としては無限ループになっているのですが、コード上で表現でき無いのが問題の一点。もう一つは startScript: が必要な部分です。これはいわば go to であり、メソッド呼び出しと違ってスタックを残しません。これは何故必要かというと、meta で呼び出した複数実行メソッド内で さらに meta を実行すると予期せぬ事態になるからです(実行すべきメソッドが倍々で増えるのを阻止している)。
簡単に言うと、Croquet の P2P プログラムというのは昔の GOTO しまくり BASIC のスパゲッティプログラムのようになってしまうという事です。Croquet 以外の P2P はこの問題(処理の輻輳とGOTOしまくり問題)をどう対処しているのだろう。もうちょっと真面目に対策を考えた方が良いんじゃないかな。