Hello,
I’m struggling with how to capture and call a block from an extension
written in Java. What I want to do is roughly equivalent to the
following
Ruby code:
def register_thing(name, &block)
@things[name] = block
end
def call_things
@things.each_value { |callback| callback.call }
end
At first what I did was this:
public IRubyObject register_thing(ThreadContext ctx, IRubyObject name,
Block block) {
things.put(name, block);
}
private void call_things(ThreadContext ctx) {
for (Block callback : things.entrySet()) {
callback.yieldSpecific(ctx);
}
}
However, I got very strange errors after running that code for a while
(see
below). I looked at how Hash#initialize captures its block and rewrote
the
code like this:
public IRubyObject register_thing(ThreadContext ctx, IRubyObject name,
Block block) {
RubyProc callback = ctx.runtime.newProc(Block.Type.PROC, block);
things.put(name, callback);
}
private void call_things(ThreadContext ctx) {
for (Block callback : things.entrySet()) {
callback.call(ctx, IRubyObject.NULL_ARRAY)
}
}
No luck, though, the problem is still there. I get different errors, but
they seem to be related to the blocks and how they get called. Here is
one:
java.lang.RuntimeException: NoVarsDynamicScope does not support scopes
with
one or more variables
at
org.jruby.runtime.scope.NoVarsDynamicScope.setValueZeroDepthZero(NoVarsDynamicScope.java:94)
at
rubyjit.MyApplication$$jvm_metric_fe000f11d0fb78e6a1e327b8436522a071de2476185645490.file(my-application.jar!/META-INF/app.home/lib/my_application.rb)
at
rubyjit.MyApplication$$jvm_metric_fe000f11d0fb78e6a1e327b8436522a071de2476185645490.file(my-application.jar!/META-INF/app.home/lib/my_application.rb)
at
org.jruby.internal.runtime.methods.JittedMethod.call(JittedMethod.java:221)
at
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:202)
at org.jruby.ast.FCallTwoArgNode.interpret(FCallTwoArgNode.java:38)
at org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
at
org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
at
org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:206)
at
org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:194)
at
org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:125)
at org.jruby.runtime.Block.call(Block.java:101)
at org.jruby.RubyProc.call(RubyProc.java:290)
at org.jruby.RubyProc.call19(RubyProc.java:271)
at multimeter.MetricRegistry$2.getValue(MetricRegistry.java:93)
at multimeter.MetricRegistry$2.getValue(MetricRegistry.java:90)
at
com.codahale.metrics.graphite.GraphiteReporter.reportGauge(GraphiteReporter.java:281)
at
com.codahale.metrics.graphite.GraphiteReporter.report(GraphiteReporter.java:171)
The error is raised from my Ruby code, but it goes through the block
calling in my extension.
Another error that is quite frequent is this one:
java.lang.ArrayIndexOutOfBoundsException: -1
at
org.jruby.runtime.ThreadContext.pushBacktrace(ThreadContext.java:565)
at
org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:110)
at
org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:206)
at
org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:194)
at
org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:125)
at org.jruby.runtime.Block.call(Block.java:101)
at org.jruby.RubyProc.call(RubyProc.java:290)
at org.jruby.RubyProc.call19(RubyProc.java:271)
at multimeter.MetricRegistry$2.getValue(MetricRegistry.java:93)
at multimeter.MetricRegistry$2.getValue(MetricRegistry.java:90)
at com.codahale.metrics.CsvReporter.reportGauge(CsvReporter.java:234)
at com.codahale.metrics.CsvReporter.report(CsvReporter.java:150)
at
com.codahale.metrics.ScheduledReporter.report(ScheduledReporter.java:162)
at
com.codahale.metrics.ScheduledReporter$1.run(ScheduledReporter.java:117)
I’ve also seen really inexplicable things happen in the blocks
themselves,
variables containing nil when they absolutely couldn’t (they are
assigned
once outside the block and then never changed), that make me think these
blocks aren’t called the right way.
You’ve probably figured out that the code examples above are complete
fabrications, the actual code doesn’t look exactly like that, but would
be
harder to show as an example. The real implementation is here:
Unfortunately I can’t show the code of the application that uses this,
but
it also doesn’t look like the problem is there.
I should also add that it worked fine in an earlier implementation that
called the Java library directly from Ruby, so there’s no problem in the
underlying Java library.
It all runs in JRuby 1.7.19.
yours,
Theo