Conversation
|
This also fixes the problem that an uninitialized (empty) |
|
In discussion on RocketChat Ref<SomeRef> pass_and_return(Ref<SomeRef> p_ref) {
return p_ref;
}causing |
|
I was testing returning references yesterday and it seems to be working now (see #949). The problem with the solution here is that the reference count is increased, with it never decreasing, resulting in leaks. It probably worked when this PR was raised because the bug on the Godot side that has been fixed wasn't increasing the reference count either, but was decreasing it, so in a way we were doing it's work here. |
|
Well I did some testing on this after fixing up the code a little, and it doesn't leak as I expected. Not 100% sure I understand why as there doesn't seem to be a matching deref but... |
|
An alternative fix can now be found in: |
|
Superseded by #958 |
This is exactly the fix proposed in #660 by @kidrigger in order to fix #652. The original PR was closed with the code removed somehow, but I believe it is a proper fix as discussed below. Another probably related PR #662 also removed the original code (only test cases left) after the merge of godotengine/godot#57968, which however didn't solve all the problems.
To summarize, when a function news and returns a new ref counted object like the code below, the object is freed immediately after the function returns so on the GDScript side we only see a null object return.
The reason is that
PtrToArgonly passes the raw pointer to the engine so the moment theRefgets destroyed it frees the object.https://github.com/zhehangd/godot-cpp/blob/master/include/godot_cpp/classes/ref.hpp#L249
The solution proposed by #660 well fixed this issue.
@BastiaanOlij mentioned a concern that this may overwrite an existing pointer.
The discussion happened several months ago and I don't the circumstance at that time, but for what I see in the current codebase this is not an issue.
PtrToArg::encodeis used to supportMethodBind::bind_ptrcall. On the engine side this is called from https://github.com/godotengine/godot/blob/b7346e50258655316a4541d17fd92cc3b3a3f6ef/modules/gdscript/gdscript_vm.cpp#L1907VariantInternal::initializeguarantees that the object pointer is cleared.In addition, I think it is always caller's duty to ensure that the dst pointer is ready to take value.
Here is a C++ snippet to test this fix
There are four functions that create a new RefCounted in different ways.
The first function returns a raw pointer without using Ref at all.
The second function is the standard way to create an object, which does not work without the fix.
The third function uses a workaround by giving it an extra reference, but this causes new troubles if other C++ functions also use it.
The fourth function converts the
Refto aVariantas return, this always works as Variant preserves the ownership inside the engine.GDScript
Here is the result for Alpha13 without the fix
is_validmeans the function returns a non-null object,is_freedmeans the object is freed after all references are lost.Clearly, without the workaround the object cannot return properly.
Somehow with the workaround the object leaks, which I don't really understand (didn't investigate, but the hacked extra reference should have be given to the engine variant), but anyway that is not the point of this PR.
This is if we apply the fix. Everything works fine. Case 3 leaks the object as expected as we have extra reference.
So that is all about this fix.
Another thing I am thinking is about is the first test case when we directly return a raw pointer.
It works almost the same as returning a
Ref, with one subtle difference that no one callsRefCounted::init_ref(not very sure).As far as I know only the constructors and assignments of
VariantandRefcallinit_refwhileptrcalldoes not go through any of them.This may expose some risks, but I don't really know about it.
So I suggest that we either fix this inconsistency or disallow returning RefCount by raw pointer. That would be another PR.