Scala の限定継続2 - Swarmによるリモート継続
Scala 2.8 の限定継続用ライブラリ Swarm を使用すると、継続をリモートプロセスに転送する事ができるようになります。
という事で、Swarm を少し試してみました。
使用した環境は以下の通り。
- Scala 2.8 Beta1
- continuations プラグイン(正式名がよくわからない)
- Swarm (c590f094f7e46674cc5effefb34f40dc43e5d415)
なお、continuations プラグインは最新の Revision:20864 でビルドに失敗するようになったので前回ビルドしたもの id:fits:20100207 を使いました。(Revision は未確認)
Swarm のビルド
まず、Swarm のソースを取得してビルドします。
なお、ビルドには Scala 2.8 と continuations プラグインのライブラリが必要です。
ソースは Git を使って取得します。
>git clone git://github.com/sanity/Swarm.git
ビルドは build.sh か Maven を使えば良さそうですが、今回は以下のような Ant のビルドファイルを自作してビルドしました。
build.xml
<project name="swarm" default="pack"> <property environment="env"/> <property file="${basedir}/plugin.properties"/> <property name="dest.dir" value="build" /> <property name="classes.dir" value="${dest.dir}/classes" /> <path id="scala.classpath"> <pathelement location="${scala.home}/lib/scala-compiler.jar"/> <pathelement location="${scala.home}/lib/scala-library.jar"/> </path> <taskdef resource="scala/tools/ant/antlib.xml" classpathref="scala.classpath"/> <target name="build"> <mkdir dir="${classes.dir}" /> <scalac srcdir="src/main/scala" destdir="${classes.dir}" includes="**/*.scala" addparams="-unchecked -Xpluginsdir ${continuation.home}/build/pack"> <classpath> <path refid="scala.classpath"/> <pathelement location="${continuation.home}/build/pack/selectivecps-library.jar" /> </classpath> </scalac> </target> <target name="pack" depends="build"> <jar destfile="${dest.dir}/swarm.jar" basedir="${classes.dir}" /> </target> ・・・ </project>
plugin.properties
scala.home=${env.SCALA_HOME} continuation.home=D:/scala_continuations
continuation.home に continuations プラグインのソースコードのホームディレクトリを設定しています。
ビルドを実施すると build/swarm.jar ファイルが作成されます。
ビルド
>ant
Swarm を使ったサンプル1
それでは、継続の機能を使って処理の途中からリモート側で実行されるような簡単なサンプルを作ってみます。
まず、継続オブジェクトが転送されてくるのを待ち受ける方の処理を作成します。以下のように Swarm.listen に待ち受けるポート番号を指定するだけです。
ListenSample.scala
import swarm._ object ListenSample { def main(args: Array[String]) = { Swarm.listen(9998) } }
次に、継続オブジェクトを転送する方の処理を作成します。
以下のように Swarm.spawn に Swarm.moveTo を内部で実行するメソッド(Unit => Bee)を渡します。
処理としては、Swarm.spawn 内で reset が、moveTo で shift が実行され、その際に継続オブジェクトがリモート転送されるため、"start proc" が MoveToSample のコンソールに、"end proc" が ListenSample のコンソールに出力されます。
MoveToSample.scala
import swarm._ import java.net.InetAddress object MoveToSample { def main(args: Array[String]) = { // Swarm.listen(9997) //listen の使用を避けるには myLocation を自前で設定する Swarm.myLocation = new Location(InetAddress.getLocalHost(), 0) Swarm.spawn(proc) } def proc(u: Unit) = { println("start proc") //サーバー側に転送 Swarm.moveTo(new Location(InetAddress.getLocalHost(), 9998)) //以降の処理がサーバー側で実行される println("end proc") NoBee() } }
上記ソースファイルを以下のようにしてコンパイルします。(continuations プラグインを指定する点に注意)
コンパイル例
>scalac -Xplugin:selectivecps-plugin.jar -classpath selectivecps-library.jar;swarm.jar ListenSample.scala >scalac -Xplugin:selectivecps-plugin.jar -classpath selectivecps-library.jar;swarm.jar MoveToSample.scala
それでは、動作確認です。
まず、以下のように ListenSample を実行し接続待ち状態にしておきます。
ListenSampleコンソール
>scala -classpath .;swarm.jar;selectivecps-library.jar ListenSample 9998 : Waiting for connection
次に、MoveToSample を実行すると以下のような結果になります。
MoveToSampleコンソール
>scala -classpath .;swarm.jar;selectivecps-library.jar MoveToSample 0 : Running task start proc 0 : Move to 0 : Moving task to 9998 0 : Transmitting task to 9998 0 : Transmission complete
ListenSample は以下のような結果になり、転送されてきた継続オブジェクトを実行している事が確認できます。
サーバー側コンソール
>scala -classpath .;swarm.jar;selectivecps-library.jar ListenSample 9998 : Waiting for connection 9998 : Received connection 9998 : Executing continuation 9998 : Running task end proc 9998 : Completed task 9998 : No more continuations to execute 9998 : Waiting for connection
なお、ListenSample 側では MoveToSample$$anonfun$proc$1.class をクラスパスに含めておく必要がある点に注意。(クラスファイルを別ディレクトリに配置している場合)
Swarm を使ったサンプル2
次に、ListenSample 側に送った継続オブジェクトを MoveToSample 側に送り返すサンプルを作成してみます。
ListenSample の変更は不要なので、以下のように moveTo を2度実行する MoveToSample2 を作成しました。
MoveToSample2.scala
import swarm._ import java.net.InetAddress object MoveToSample2 { def main(args: Array[String]) = { Swarm.listen(9997) Swarm.spawn(proc) } def proc(u: Unit) = { //ローカル側(MoveToSample2)で実行 var i = 0 println("start proc : " + i) //リモート側(ListenSample)に転送 Swarm.moveTo(new Location(InetAddress.getLocalHost(), 9998)) //リモート側(ListenSample)で実行 i = i + 5 println("remote proc : " + i) //ローカル側(MoveToSample2)に転送 Swarm.moveTo(new Location(InetAddress.getLocalHost(), 9997)) //ローカル側(MoveToSample2)で実行 i = i + 10 println("end proc : " + i) NoBee() } }
実行結果は以下のようになります。
ListenSample 側の結果
>scala -classpath .;swarm.jar;selectivecps-library.jar ListenSample 9998 : Waiting for connection 9998 : Received connection 9998 : Executing continuation 9998 : Running task remote proc : 5 9998 : Move to 9998 : Moving task to 9997 9998 : Transmitting task to 9997 9998 : Transmission complete 9998 : Waiting for connection
MoveToSample2 側の結果
>scala -classpath .;swarm.jar;selectivecps-library.jar MoveToSample2 9997 : Waiting for connection 9997 : Running task start proc : 0 9997 : Move to 9997 : Moving task to 9998 9997 : Transmitting task to 9998 9997 : Transmission complete 9997 : Received connection 9997 : Executing continuation 9997 : Running task end proc : 15 9997 : Completed task 9997 : No more continuations to execute 9997 : Waiting for connection