前回、RoarVM 専用並列処理特化 DSL である Sly3 について、「アンサンブルが同名メソッドを持っていなければ、副詞 IndividualLY が無くてもメッセージはアンサンブル内の各要素に委譲される」と書きましたが、あれは嘘でした。すみません。
blocks copy fixTemps
と複数のブロックを収めたアンサンブルである blocks に対して、アンサンブルにない BlockContext 固有の fixTemps はともかく、誰でも反応できるはずの copy を送っても IndividualLY になるのはおかしいな…と気になって調べてみたところ、完全な思い違いが判明しました。お恥ずかしい。
そもそもこの勘違いのきっかけは、Sly3Ensemble に #doesNotUnderstand: が(再)定義されているのを見つけて、てっきり自分が理解できないメッセージについてのみその要素にメッセージを転送する、よくある仕組みだろうと早合点したことでした。
実際は、アンサンブルを構成する Sly3Ensemble>>#members: 内で Object>>#primitiveSetExtraWordSelector: および Object>>#primitiveSetExtraPreheaderWord: をコールすることでプリミティブレベルで自身の情報を書き換えることで、自身に送られたメッセージは(先述の、よくある doesNotUnderstand: の再定義による仕組みを介することなしに) members に収められたコレクションに sentToEnsemble: aMessage をコールするかたちで無条件に委譲される仕組みになっていました。(これだけだとアンサンブルを構成するコレクションに委譲されるだけですが、さらに先の処理で一定の条件を満たした場合に各要素に委譲されるようです。)
Sly3Ensemble >> members: aCollection "crude check:" self primitiveSetExtraWordSelector: #sentToEnsemble:. aCollection primitiveSetExtraPreheaderWord: self. members _ aCollection. ^ self
ではなぜ通常は使う必要がない #doesNotUnderstand: が再定義されているかというと、これも当該メソッドのソースにちゃんと説明が書いてありました。^^;
Sly3Ensemble >> doesNotUnderstand: aMessage "Try to process this message as an ensemble dispatch message to help in the case of inlined primitive calls like perform:with:with:" ^ Sly3EnsembleMessageDispatcher dispatch: (Sly3UnprocessedEnsembleMessage fromMessage: aMessage theEnsemble: self)
通常のメッセージ式ではなく、#perform: などを使ってアンサンブルにメッセージが送られた場合であっても、同様の仕組みが(もとより、Sly3Ensemble に定義されていないメソッドのコールに限られるため、不完全ではありますが―)機能するように備えたもの…ということらしいです。
ただ残念ながら、デバッガーで使われるインタープリターシミュレーターにはこうした配慮が行き届いていない(あるいは当該機構によりインタープリターシミュレーターがバグる?)ようで、結果的にデバッガーでステップ実行したときと通常実行したときで動きが異なる事態が発生してしまうみたいなので要注意です。
関連してデバッガーについては、これを動作させようとすると並列処理に起因すると思われるノーティファイアーの嵐が出まくって操作不能に陥ることが多々あり、やはりプロセス(スレッド)を多コア対応にできても Squeak Smalltalk 環境を既存のコードのまま運用するのは難しく、全体の書き直しが必要でそれはきっと大変な作業になるのだろうな…と実感させられます。RoarVM/Sly3 評価用の renaissance.image が かなり手間をかけてMVC ベースや、ブロックがクロージャーでなくすなど過去の Smalltalk の状態に戻してしてあったりするのも、並列処理に起因するトラブルにできるだけシンプルな対応で済ませたいがためだったのかもしれませんね。
次回へ続く。