org.jruby.Rubyをシンクロして使う。 2008/09/04

JRubyです。

org.jruby.Rubyオブジェクトを使いまわしてスクリプトを実行する際に、グローバル変数を使ってJRubyに値を渡すとき、org.jruby.Rubyをシンクロしないといけないよというお話。

以下、スレッドをぽこぽこ起こして、org.jruby.Rubyにグローバル変数経由で値をわたすと意図せずまざります。
org.jruby.Rubyを生成するコストはばかにならないので、newしてぽこぽこ生成したくはないです。

あたりまえといえばあたりまえですが、使うorg.jruby.Rubyをsynchronizedブロックにしてしまいます。

テスト用のjrubyのスクリプト

p $a + ' ' + $b + ' ' + $c + ' ' + $d + ' ' + $e + ' ' + $f + ' ' + $g + ' ' + $h


このスクリプトの意図はグローバル変数が途中で書き換わると、値に一貫性がなくなることをチェックするためです。

シンクロしないとものの見事に、一貫性がなくなることが確認されます。

以下うまくいったコード

package test_jruby;

import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;

public class Test_multi {

public static void main(String[] args) throws IOException {

final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {

final int FI = i;
new Thread(new Runnable() {

@Override
public void run() {

String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });

System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}

}

static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}

/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {

synchronized (ruby) {

if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {

ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}

}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();

}

}
}


以下うまくいかなかったコード
org.jruby.RubyのdefineVariableをする間隔で、Tread.sleepさせるとさらにまざる感じがします。

package test_jruby;

import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.bsf.util.IOUtils;
import org.jruby.Ruby;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.GlobalVariable;
import org.jruby.runtime.builtin.IRubyObject;

public class Test_multi {

public static void main(String[] args) throws IOException {

final Ruby ruby = Ruby.newInstance();
final String script = script("test_multi_glv_1.jruby", "MS932");
for (int i = 0; i < 10000; i++) {

final int FI = i;
new Thread(new Runnable() {

@Override
public void run() {

String result = evalByJRuby_GLV(ruby, script,
new Object[][] { { "a", "" + FI },
{ "b", "" + FI }, { "c", "" + FI },
{ "d", "" + FI }, { "e", "" + FI },
{ "f", "" + FI }, { "g", "" + FI },
{ "h", "" + FI } });

System.out.println(result);
}
}).start();
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}

}

static public String script(String filename, String enc) throws IOException {
String script = IOUtils.getStringFromReader(new InputStreamReader(
ExperimentationJRubyScriptBase.class
.getResourceAsStream(filename), enc));
return script;
}

/**
* <p>
* Rubyスクリプトにグローバル変数で値を渡します。
* </p>
*
* @param ruby
* @param script
* @param objects
* @return
*/
public static String evalByJRuby_GLV(Ruby ruby, String script,
Object[][] objects) {

// synchronized (ruby) {

if (objects != null) {
for (int i = 0; i < objects.length; i++) {
Object[] objects2 = objects[i];
if (2 <= objects2.length) {

ruby.defineVariable(new GlobalVariable(ruby, "$"
+ objects2[0].toString(), JavaEmbedUtils
.javaToRuby(ruby, objects2[1])));
}
}

}
IRubyObject result = ruby.evalScriptlet(script);
ruby.tearDown();
if (result instanceof org.jruby.RubyString) {
org.jruby.RubyString s = (org.jruby.RubyString) result;
return s.getUnicodeValue();
}
return result.toString();

// }

}
}

: