-
Notifications
You must be signed in to change notification settings - Fork 82
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
Add tests cases for, and fix, #513 #524
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The changes in d1d6ca4 made several `Geometry2DTests` fail. The actual unbalanced retain causing the leak in `VariantRepresentable.swift`: ``` swift extension ContentVariantRepresentable { public init? (_ variant: Variant) { guard Self.godotType == variant.gtype else { return nil } var content = Self.zero withUnsafeMutablePointer(to: &content) { ptr in variant.toType(Self.godotType, dest: ptr) // <-- THIS IS THE UNBALANCED RETAIN } self.init(content: content) } } ``` The `variant.toType` function calls into Godot to copy out the built-in type stored in `variant`. If that built-in type has an internal reference count, Godot increments it. Then, the call to `self.init(content: content)` increments the count again. The retain performed by `variant.toType` is never balanced. This patch fixes the bug by generating a new `init(alreadyOwnedContent:)` initializer for each Godot builtin class type. This needs to be on the builtin wrappers (like `GDictionary`, `GArray`, etc.), rather than on `Variant` which is where d1d6ca4 put it. This patch also adds a test case to check for the leak by looking at Godot's `MEMORY_STATIC` performance counter, which is only enabled if Godot was built with `DEBUG_ENABLED`.
Closed
This patch adds a test for the second leak described in migueldeicaza#513, and fixes the leak. Without this patch, the leak happens because `Variant.asObject` has an unneeded call to `rc.reference()` which increments the `RefCounted` object's reference count. As far as I can tell, nothing balances this increment. `Variant.asObject` calls `lookupObject(nativeHandle:)`, which returns a Swift wrapper for an object whose reference count has already been incremented (if the object is `RefCounted`). The Swift wrapper balances that increment with a decrement in its `deinit`.
You are absolutely right about these observations, I ran into these while cooking the tests, and noticed the 2D tests fail and spent some quality time trying to figure it out, when I saw that I decided to check on the bug again and saw your analysis. Love your patch, and the additional insight on the differences on those built-in types. |
migueldeicaza
pushed a commit
that referenced
this pull request
Jan 19, 2025
* add some support for using a local libgodot build * fix better the first leak of #513 The changes in d1d6ca4 made several `Geometry2DTests` fail. The actual unbalanced retain causing the leak in `VariantRepresentable.swift`: ``` swift extension ContentVariantRepresentable { public init? (_ variant: Variant) { guard Self.godotType == variant.gtype else { return nil } var content = Self.zero withUnsafeMutablePointer(to: &content) { ptr in variant.toType(Self.godotType, dest: ptr) // <-- THIS IS THE UNBALANCED RETAIN } self.init(content: content) } } ``` The `variant.toType` function calls into Godot to copy out the built-in type stored in `variant`. If that built-in type has an internal reference count, Godot increments it. Then, the call to `self.init(content: content)` increments the count again. The retain performed by `variant.toType` is never balanced. This patch fixes the bug by generating a new `init(alreadyOwnedContent:)` initializer for each Godot builtin class type. This needs to be on the builtin wrappers (like `GDictionary`, `GArray`, etc.), rather than on `Variant` which is where d1d6ca4 put it. This patch also adds a test case to check for the leak by looking at Godot's `MEMORY_STATIC` performance counter, which is only enabled if Godot was built with `DEBUG_ENABLED`. * fix second leak of #513 This patch adds a test for the second leak described in #513, and fixes the leak. Without this patch, the leak happens because `Variant.asObject` has an unneeded call to `rc.reference()` which increments the `RefCounted` object's reference count. As far as I can tell, nothing balances this increment. `Variant.asObject` calls `lookupObject(nativeHandle:)`, which returns a Swift wrapper for an object whose reference count has already been incremented (if the object is `RefCounted`). The Swift wrapper balances that increment with a decrement in its `deinit`. --------- Co-authored-by: Rob Mayoff <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This patch addresses both leaks discussed in #513.
First leak
The changes in d1d6ca4 made several
Geometry2DTests
fail.The actual unbalanced retain causing the leak in
VariantRepresentable.swift
:The
variant.toType
function calls into Godot to copy out the built-in type stored invariant
. If that built-in type has an internal reference count, Godot increments it. Then, the call toself.init(content: content)
increments the count again. The retain performed byvariant.toType
is never balanced.This patch fixes the bug by generating a new
init(alreadyOwnedContent:)
initializer for each Godot builtin class type. This needs to be on the builtin wrappers (likeGDictionary
,GArray
, etc.), rather than onVariant
which is where d1d6ca4 put it.This patch also adds a test case to check for the leak by looking at Godot's
MEMORY_STATIC
performance counter, which is only enabled if Godot was built withDEBUG_ENABLED
.Second leak
This patch adds a test for the second leak described in #513, and fixes the leak.
Without this patch, the leak happens because
Variant.asObject
has an unneeded call torc.reference()
which increments theRefCounted
object's reference count. As far as I can tell, nothing balances this increment.Variant.asObject
callslookupObject(nativeHandle:)
, which returns a Swift wrapper for an object whose reference count has already been incremented (if the object isRefCounted
). The Swift wrapper balances that increment with a decrement in itsdeinit
.