Skip to content

Commit 606438b

Browse files
committed
[GR-25264] Implement inner context onCancelled callback to raise an exception when stopped
PullRequest: truffleruby/3305
2 parents 4a02b59 + 96155c9 commit 606438b

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

spec/truffle/interop/polyglot/inner_context_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,37 @@
5050
-> { context.eval('does_not_exist', '') }.should raise_error(ArgumentError, 'Unknown language: does_not_exist')
5151
end
5252
end
53+
54+
it "raises a RuntimeError when stopped" do
55+
context = Polyglot::InnerContext.new
56+
context.eval('ruby', '42') # Eagerly initializes the context to avoid stopping during context initialization
57+
58+
in_synchronize = false
59+
th = Thread.new do
60+
Thread.pass until in_synchronize
61+
context.stop
62+
end
63+
-> {
64+
in_synchronize = true
65+
context.eval('ruby', 'loop { }')
66+
}.should raise_error(RuntimeError, 'Polyglot::InnerContext was terminated forcefully')
67+
th.join
68+
end
69+
70+
it "calls the given on_cancelled proc when stopped" do
71+
custom_error = Class.new(StandardError)
72+
context = Polyglot::InnerContext.new(on_cancelled: -> { raise custom_error, 'error message' })
73+
context.eval('ruby', '42') # Eagerly initializes the context to avoid stopping during context initialization
74+
75+
in_synchronize = false
76+
th = Thread.new do
77+
Thread.pass until in_synchronize
78+
context.stop
79+
end
80+
-> {
81+
in_synchronize = true
82+
context.eval('ruby', 'loop { }')
83+
}.should raise_error(custom_error, 'error message')
84+
th.join
85+
end
5386
end

src/main/java/org/truffleruby/interop/PolyglotNodes.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.truffleruby.builtins.Primitive;
2222
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
2323
import org.truffleruby.core.klass.RubyClass;
24+
import org.truffleruby.core.proc.RubyProc;
2425
import org.truffleruby.core.rope.Rope;
2526
import org.truffleruby.core.rope.RopeNodes;
2627
import org.truffleruby.core.rope.RopeOperations;
@@ -40,6 +41,7 @@
4041
import com.oracle.truffle.api.nodes.IndirectCallNode;
4142
import com.oracle.truffle.api.source.Source;
4243
import org.truffleruby.language.library.RubyStringLibrary;
44+
import org.truffleruby.language.yield.CallBlockNode;
4345
import org.truffleruby.utils.Utils;
4446

4547
@CoreModule("Polyglot")
@@ -169,11 +171,16 @@ private Source getSource(String language, String fileName) {
169171

170172
@Primitive(name = "inner_context_new")
171173
public abstract static class InnerContextNewNode extends PrimitiveArrayArgumentsNode {
174+
175+
@TruffleBoundary
172176
@Specialization
173-
protected RubyInnerContext newInnerContext(RubyClass rubyClass) {
177+
protected RubyInnerContext newInnerContext(RubyClass rubyClass, RubyProc onCancelledCallback) {
174178
final TruffleContext innerContext = getContext()
175179
.getEnv()
176180
.newContextBuilder()
181+
.onCancelled(() -> {
182+
CallBlockNode.getUncached().yield(onCancelledCallback);
183+
})
177184
.initializeCreatorContext(false)
178185
.build();
179186

@@ -182,6 +189,7 @@ protected RubyInnerContext newInnerContext(RubyClass rubyClass) {
182189
getLanguage().innerContextShape,
183190
innerContext);
184191
}
192+
185193
}
186194

187195
@Primitive(name = "inner_context_eval")

src/main/ruby/truffleruby/core/truffle/polyglot.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class InnerContext
1717
# Create a new isolated inner context to eval code in any available public language
1818
# (those languages can be listed with +Polyglot.languages+).
1919
# Automatically closes the context when given a block.
20-
def self.new
21-
inner_context = Primitive.inner_context_new(self)
20+
def self.new(on_cancelled: -> { raise RuntimeError, 'Polyglot::InnerContext was terminated forcefully' })
21+
inner_context = Primitive.inner_context_new(self, on_cancelled)
2222
if block_given?
2323
begin
2424
yield inner_context

0 commit comments

Comments
 (0)