-
-
Notifications
You must be signed in to change notification settings - Fork 210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix early destruction of objects returned from Rust (#138) #165
Conversation
Thanks! The commit you referred to occured as part of #112, which was motivated in fixing ref-count related problems. I wrote tests that failed before and passed after the PR. You understand I cannot just revert the changes that evidently brought improvements, while the CI of this pull request is red. It's well possible that there is a mistake in #112 though, but we need to approach it more scientifically:
So before that, can you exclude that there is a configuration problem on your side? Are you using Godot |
We still have the comment Could you try to distill the problem that occurs in #138 into something testable, namely an |
@@ -163,7 +163,7 @@ macro_rules! impl_signature_for_tuple { | |||
.unwrap_or_else(|e| return_error::<$R>(method_name, &e)); | |||
|
|||
// FIXME is inc_ref needed here? | |||
// std::mem::forget(ret_val); | |||
std::mem::forget(ret_val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is the only thing that's needed to stop #138 from happening. It worked on my machine at least. Even the sanitizer build was happy, whereas before it would complain about a heap-use-after-free (it seems the array would get dropped here.)
I just ran a full-ci after making this change too, and everything is green.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FIXME
points to the open question: is mem::forget
is the right approach, or should we increment the ref-count?
This just disables the destructor, but I think the proper way would be to create a (ref) copy and then run the destructor. I'm not sure if there's a difference though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just disables the destructor, but I think the proper way would be to create a (ref) copy and then run the destructor. I'm not sure if there's a difference though.
So from what i can tell, the destructor of Array
is just a call to unref
internally. So there would be no difference between calling inc_ref
, then dropping it, and just mem::forget
. At least for Array (and i believe other ref-counted types too).
But either way, the purpose of mem::forget
is to ensure the destructor isn't run. And that's what we want here right? We're returning the value, so the destructor should not be run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's leave it like this then.
@thebigG nitpick, could you align the //
vertically and add a space between //
and Note:
?
@thebigG Could you implement the suggestion from @SayakS (only Would still be good to include the test case that triggers the original problem -- i.e. red before this change, green after. Thanks! |
@Bromeon Will do. |
I'm not sure if the tests approach I took is the best. I have code duplication. So perhaps you guys have some feedback on how to make that better :) |
Quick note not directly related to this PR: Do we currently have a way in the tests to catch the following kinds of errors from Godot(?):
It so happens in my array test, the error with ref_count does get "caught "(though it is caught only because there is a segfault, it looks like anyway, if one tries to access the array in the assertion). But I suspect there might be certain "errors" from Godot in the future that just get "printed" and might get difficult to validate those errors in unit tests.... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really see why you add a whole new file InheritTests.gd
. Could you not simply add the test case test_vector_array_return_from_user_func
to the existing ManualFfiTests.gd
?
Do we currently have a way in the tests to catch the following kinds of errors from Godot(?):
Unfortunately not, Godot doesn't expose error handlers to GDExtension. But in Godot, the philosophy is that many errors can be avoided by previously checking some condition, although many times at extra performance cost.
Please also squash the commits to 1 🙂
itest/godot/InheritTests.gd
Outdated
var _assertion_failed: bool = false | ||
|
||
## Asserts that `what` is `true`, but does not abort the test. Returns `what` so you can return | ||
## early from the test function if the assertion failed. | ||
func assert_that(what: bool, message: String = "") -> bool: | ||
if what: | ||
return true | ||
|
||
_assertion_failed = true | ||
if message: | ||
print("assertion failed: %s" % message) | ||
else: | ||
print("assertion failed") | ||
return false | ||
|
||
func assert_eq(left, right, message: String = "") -> bool: | ||
if left == right: | ||
return true | ||
|
||
_assertion_failed = true | ||
if message: | ||
print("assertion failed: %s\n left: %s\n right: %s" % [message, left, right]) | ||
else: | ||
print("assertion failed: `(left == right)`\n left: %s\n right: %s" % [left, right]) | ||
return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should just be able to reuse TestSuite
, as it's done here:
gdext/itest/godot/ManualFfiTests.gd
Line 5 in c1de3f7
extends TestSuite |
Or even better, as mentioned above just move it to ManualFfiTests.gd
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even better, as mentioned above just move it to
ManualFfiTests.gd
.
This behavior is only triggered when the function is called from a script, which inherits the object/node the function belongs to. This was verified here as well:#138 (comment)
Which is why I can't move this code to ManualFfiTests.gd
. Unless multiple inheritance is allowed in GDScript(?)
I don't really see why you add a whole new file
InheritTests.gd
. Could you not simply add the test casetest_vector_array_return_from_user_func
to the existingManualFfiTests.gd
?
No
ccfae72
to
418f6d3
Compare
@Bromeon merged all commits into one. Let me know of any other feedback you might have :). I know the unit test adds an extra file, but can't think of another way of doing this.... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Some comments:
- The new file could have a variable of type
TestSuite
, meaning you could reuse that code via composition. Then just declare two one-liner functionsassert_that
andassert_eq
which forward toself.test_suite.<method>
. - Since the test must extend ArrayTest in order to reproduce the behavior, please comment this rationale at the beginning of the file (e.g. why this choice, why we can't reuse the asserts, etc).
- Each file must start with the license header on the very top, see other .gd files 🙂
418f6d3
to
489888b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally good, but some typos need fixing.
itest/godot/InheritTests.gd
Outdated
|
||
extends ArrayTest | ||
|
||
var test_siute: TestSuite = TestSuite.new() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
itest/godot/InheritTests.gd
Outdated
# Since in order to reproduce the bahevaior discovered in https://github.com/godot-rust/gdext/issues/138 | ||
# We must inherit a Godot Node. Because of this, we can't just inherit TesSuite like the rest of the tests. | ||
# Because of that, we get the TestSuite code via composition rather than iheritance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3 orthographic erros, please re-read comments quickly 🙂
@@ -163,7 +163,7 @@ macro_rules! impl_signature_for_tuple { | |||
.unwrap_or_else(|e| return_error::<$R>(method_name, &e)); | |||
|
|||
// FIXME is inc_ref needed here? | |||
// std::mem::forget(ret_val); | |||
std::mem::forget(ret_val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's leave it like this then.
@thebigG nitpick, could you align the //
vertically and add a space between //
and Note:
?
489888b
to
30384e5
Compare
Addressed all concerns. Thanks a lot for the great feedback @Bromeon 👍 . |
You still write "bahevaior" and CI is also failing, due to another typo. Please double-check these things and make sure CI is green, this would save a lot of time for both of us. |
30384e5
to
8b7b786
Compare
8b7b786
to
4906bea
Compare
Thanks a lot! |
165: Fix early destruction of objects returned from Rust (#138) r=Bromeon a=thebigG Hi there, I probably need to test this more thoroughly. Just opening a pull request for collaboration and such. Basically it came down to for some reason(I'm still learning about this rust port of godot) ac02be1 triggers this behavior. And this PR reverts that commit(except for the tests). This patch fixes my use case, but I'll try to write unit tests and test in general to make sure we don't miss any other edge cases. Hope this PR is clear enough. Thanks Lorenzo Co-authored-by: thebigG <[email protected]>
Build failed: |
CI failed. I cannot merge this change as it is now -- while it fixes UB in some cases, it introduces memory leaks in others 😢 Clearly this needs a bit more thought... |
Just to note here too, i did some digging in a discord thread and it does seem like Basically godot is giving us an initialized |
204: Fix various issues with pointer calls r=Bromeon a=sayaks Combines #165 and #200 with more complete fixes. # Overview Properly clones strings and shares arrays/dictionaries in pointer calls to ensure they're not prematurely freed. Frees the value currently in the `ret` pointer in pointer calls, to avoid memory leak. Moves `ret_val` into `ret`, to avoid premature destruction. Changes integer conversions in pointer calls to use `as`, since that seems like the right way of converting the values we get from godot. `try_from` lead to wrong conversions sometimes. Objects inheriting from `RefCounted` now use `ref_get_object` and `ref_set_object` in virtual method calls. # Details Add `from_arg_ptr` and `move_return_ptr` to `GodotFfi` that will be used when converting argument pointers in pointer calls, and moving things into a pointer. Add `CallType` so `from_arg_ptr` and `move_return_ptr` can have different behavior depending on whether it's a virtual method call or not. Remove `write_sys` in `GodotFfi`. Override `from_arg_ptr` in `GodotString`, `NodePath`, `StringName`, `Array`, `Dictionary`, `Gd`. Override `move_return_ptr` in `Gd`. Rename `try_write_sys` to `try_return`, and have it use `move_return_ptr` instead of `write_sys`. Rename `try_from_sys` to `try_from_arg`, and have it use `from_arg_ptr` instead of `from_sys`. Add `u8`, `u16`, `u32` to the ptrcall tests. Add a couple of test of virtual methods. Fix a test for virtual method with return values to actually call the virtual method as a virtual method. closes #195 closes #189 Co-authored-by: Jan Haller <[email protected]> Co-authored-by: thebigG <[email protected]> Co-authored-by: Lili Zoey <[email protected]>
Merged as part of #204. |
Hi there,
I probably need to test this more thoroughly. Just opening a pull request for collaboration and such.
Basically it came down to for some reason(I'm still learning about this rust port of godot) ac02be1 triggers this behavior. And this PR reverts that commit(except for the tests).
This patch fixes my use case, but I'll try to write unit tests and test in general to make sure we don't miss any other edge cases.
Hope this PR is clear enough.
Thanks
Lorenzo