Skip to content

Commit 08e3887

Browse files
committed
Unwrap values returned from C before popping the extension stack.
1 parent 20b6cc7 commit 08e3887

File tree

4 files changed

+70
-22
lines changed

4 files changed

+70
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Bug fixes:
2020
* Fix `Random#rand` not returning random floats when given float ranges (#2612, @bjfish).
2121
* Fix `Array#sample` for `[]` when called without `n` and a `Random` is given (#2612, @bjfish).
2222
* Fix `Module#const_get` to raise a `NameError` when nested modules do not exist (#2610, @bjfish).
23+
* Ensure native `VALUE`s returned from C are unwrapped before the objects can be collected (@aardvark179).
2324

2425
Compatibility:
2526

lib/truffle/truffle/cext.rb

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -975,14 +975,13 @@ def rb_path_to_class(path)
975975

976976
def rb_proc_new(function, value)
977977
Proc.new do |*args, &block|
978-
Primitive.cext_unwrap(
979-
Primitive.call_with_c_mutex_and_frame(function, [
980-
Primitive.cext_wrap(args.first), # yieldarg
981-
Primitive.cext_wrap(value), # procarg,
982-
args.size, # argc
983-
Truffle::CExt.RARRAY_PTR(args), # argv
984-
Primitive.cext_wrap(block), # blockarg
985-
], Primitive.caller_special_variables_if_available, nil))
978+
Primitive.call_with_c_mutex_and_frame_and_unwrap(function, [
979+
Primitive.cext_wrap(args.first), # yieldarg
980+
Primitive.cext_wrap(value), # procarg,
981+
args.size, # argc
982+
Truffle::CExt.RARRAY_PTR(args), # argv
983+
Primitive.cext_wrap(block), # blockarg
984+
], Primitive.caller_special_variables_if_available, nil)
986985
end
987986
end
988987

@@ -1227,7 +1226,7 @@ def rb_enumeratorize(obj, meth, args)
12271226
def rb_enumeratorize_with_size(obj, meth, args, size_fn)
12281227
return rb_enumeratorize(obj, meth, args) if size_fn.nil?
12291228
enum = obj.to_enum(meth, *args) do
1230-
Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(size_fn, [Primitive.cext_wrap(obj), Primitive.cext_wrap(args), Primitive.cext_wrap(enum)], Primitive.caller_special_variables_if_available, nil))
1229+
Primitive.call_with_c_mutex_and_frame_and_unwrap(size_fn, [Primitive.cext_wrap(obj), Primitive.cext_wrap(args), Primitive.cext_wrap(enum)], Primitive.caller_special_variables_if_available, nil)
12311230
end
12321231
enum
12331232
end
@@ -1242,7 +1241,7 @@ def rb_newobj_of(ruby_class)
12421241

12431242
def rb_define_alloc_func(ruby_class, function)
12441243
ruby_class.singleton_class.define_method(:__allocate__) do
1245-
Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(function, [Primitive.cext_wrap(self)], Primitive.caller_special_variables_if_available, nil))
1244+
Primitive.call_with_c_mutex_and_frame_and_unwrap(function, [Primitive.cext_wrap(self)], Primitive.caller_special_variables_if_available, nil)
12461245
end
12471246
class << ruby_class
12481247
private :__allocate__
@@ -1667,13 +1666,13 @@ def rb_thread_call_without_gvl(function, data1, unblock, data2)
16671666
def rb_iterate(iteration, iterated_object, callback, callback_arg)
16681667
block = rb_block_proc
16691668
wrapped_callback = proc do |block_arg|
1670-
Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(callback, [
1669+
Primitive.call_with_c_mutex_and_frame_and_unwrap(callback, [
16711670
Primitive.cext_wrap(block_arg),
16721671
Primitive.cext_wrap(callback_arg),
16731672
0, # argc
16741673
nil, # argv
16751674
nil, # blockarg
1676-
], Primitive.cext_special_variables_from_stack, block))
1675+
], Primitive.cext_special_variables_from_stack, block)
16771676
end
16781677
Primitive.cext_unwrap(
16791678
Primitive.call_with_c_mutex_and_frame(iteration, [Primitive.cext_wrap(iterated_object)], Primitive.cext_special_variables_from_stack, wrapped_callback))
@@ -1785,7 +1784,7 @@ def rb_define_hooked_variable(name, gvar, getter, setter)
17851784
id = name.to_sym
17861785

17871786
getter_proc = -> {
1788-
Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(getter, [Primitive.cext_wrap(id), gvar, Primitive.cext_wrap(nil)], Primitive.caller_special_variables_if_available, nil))
1787+
Primitive.call_with_c_mutex_and_frame_and_unwrap(getter, [Primitive.cext_wrap(id), gvar, Primitive.cext_wrap(nil)], Primitive.caller_special_variables_if_available, nil)
17891788
}
17901789

17911790
setter_proc = -> value {
@@ -1910,14 +1909,13 @@ def rb_fiber_current
19101909

19111910
def rb_fiber_new(function, value)
19121911
Fiber.new do |*args|
1913-
Primitive.cext_unwrap(
1914-
Primitive.call_with_c_mutex_and_frame(function, [
1915-
Primitive.cext_wrap(args.first), # yieldarg
1916-
nil, # procarg,
1917-
0, # argc
1918-
nil, # argv
1919-
nil, # blockarg
1920-
], nil, nil))
1912+
Primitive.call_with_c_mutex_and_frame_and_unwrap(function, [
1913+
Primitive.cext_wrap(args.first), # yieldarg
1914+
nil, # procarg,
1915+
0, # argc
1916+
nil, # argv
1917+
nil, # blockarg
1918+
], nil, nil)
19211919
end
19221920
end
19231921

lib/truffle/truffle/cext_ruby.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def rb_define_method(mod, name, function, argc)
3838
# We must set block argument if given here so that the
3939
# `rb_block_*` functions will be able to find it by walking the
4040
# stack.
41-
res = Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(function, args, Primitive.caller_special_variables_if_available, block))
41+
res = Primitive.call_with_c_mutex_and_frame_and_unwrap(function, args, Primitive.caller_special_variables_if_available, block)
4242
Primitive.thread_set_exception(exc)
4343
res
4444
end

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,55 @@ protected Object callWithCExtLockAndFrame(
159159
}
160160
}
161161

162+
@Primitive(name = "call_with_c_mutex_and_frame_and_unwrap")
163+
public abstract static class CallWithCExtLockAndFrameAndUnwrapNode extends PrimitiveArrayArgumentsNode {
164+
165+
@Specialization(limit = "getCacheLimit()")
166+
protected Object callWithCExtLockAndFrame(
167+
VirtualFrame frame, Object receiver, RubyArray argsArray, Object specialVariables, Object block,
168+
@CachedLibrary("receiver") InteropLibrary receivers,
169+
@Cached ArrayToObjectArrayNode arrayToObjectArrayNode,
170+
@Cached TranslateInteropExceptionNode translateInteropExceptionNode,
171+
@Cached ConditionProfile ownedProfile,
172+
@Cached UnwrapNode unwrapNode) {
173+
final ExtensionCallStack extensionStack = getLanguage()
174+
.getCurrentThread()
175+
.getCurrentFiber().extensionCallStack;
176+
final boolean keywordsGiven = RubyArguments.getDescriptor(frame) instanceof KeywordArgumentsDescriptor;
177+
extensionStack.push(keywordsGiven, specialVariables, block);
178+
try {
179+
final Object[] args = arrayToObjectArrayNode.executeToObjectArray(argsArray);
180+
181+
if (getContext().getOptions().CEXT_LOCK) {
182+
final ReentrantLock lock = getContext().getCExtensionsLock();
183+
boolean owned = ownedProfile.profile(lock.isHeldByCurrentThread());
184+
185+
if (!owned) {
186+
MutexOperations.lockInternal(getContext(), lock, this);
187+
}
188+
try {
189+
return unwrapNode.execute(
190+
InteropNodes.execute(receiver, args, receivers, translateInteropExceptionNode));
191+
} finally {
192+
if (!owned) {
193+
MutexOperations.unlockInternal(lock);
194+
}
195+
}
196+
} else {
197+
return unwrapNode
198+
.execute(InteropNodes.execute(receiver, args, receivers, translateInteropExceptionNode));
199+
}
200+
201+
} finally {
202+
extensionStack.pop();
203+
}
204+
}
205+
206+
protected int getCacheLimit() {
207+
return getLanguage().options.DISPATCH_CACHE;
208+
}
209+
}
210+
162211
@Primitive(name = "call_with_c_mutex")
163212
public abstract static class CallWithCExtLockNode extends PrimitiveArrayArgumentsNode {
164213

0 commit comments

Comments
 (0)