Skip to content
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

Bug Report:Memory leak with passing lua functions to GDScript #155

Closed
RadiantUwU opened this issue Sep 8, 2023 · 22 comments · Fixed by #180
Closed

Bug Report:Memory leak with passing lua functions to GDScript #155

RadiantUwU opened this issue Sep 8, 2023 · 22 comments · Fixed by #180
Labels
bug Something isn't working

Comments

@RadiantUwU
Copy link
Contributor

Describe the bug
This bug happens by creating constant references and not being able to know when the reference is removed or not.

To Reproduce

extends Node

func do_something_with_callable(callable: Callable):
	pass

# Called when the node enters the scene tree for the first time.
func _ready():
	var api = LuaAPI.new()
	api.push_variant("do_something_with_callable",do_something_with_callable)
	api.do_string("""
	while true do
		do_something_with_callable(function() end)
	end
	""")

Expected behavior
The memory usage should remain stable, not constantly rising.

Environment (please complete the following information):

  • OS: Linux 6.4.12-arch1-1
  • Godot version: v4.1.1.stable.official [bd6af8e0e]
  • Module version: v2.1-beta4

Additional context
Add any other context about the problem here.
image
Very quickly rising memory usages from 200 MB to 500MB within 5 seconds, potentially more if left longer.

@RadiantUwU RadiantUwU added the bug Something isn't working label Sep 8, 2023
@Trey2k
Copy link
Member

Trey2k commented Sep 8, 2023

So this is a known memory leak, the issue is without the CallableCustom type we have no way to have a deconstructor tied to the callable, meaning a copy of the lua function remains on the state.

This should hopefully be resolved once godotengine/godot#79005 is merged, and we can use the CallableCustom type.

@RadiantUwU
Copy link
Contributor Author

image
image

@RadiantUwU
Copy link
Contributor Author

So this is a known memory leak, the issue is without the CallableCustom type we have no way to have a deconstructor tied to the callable, meaning a copy of the lua function remains on the state.

This should hopefully me resolved once godotengine/godot#79005 is merged, and we can use the CallableCustom type.

Make a new refcounted and pass it as the instance for creating the callable, give it the reference and make it so when the refcounted disappears it clears it from the registry.

@RadiantUwU
Copy link
Contributor Author

Should i try doing a pull request...?

@Trey2k
Copy link
Member

Trey2k commented Sep 8, 2023

Make a new refcounted and pass it as the instance for creating the callable, give it the reference and make it so when the refcounted disappears it clears it from the registry.

This is something I had attempted, feel free to try. But check out this issue as well godotengine/godot#74990

@dsnopek
Copy link

dsnopek commented Sep 8, 2023

If you could test the CustomCallable PR (godotengine/godot#79005) with this language binding and report whether or not it was sufficient for your needs, that could help get it merged. :-) The main thing holding back that PR at the moment, is that we're waiting for other language bindings to try it out and give feedback.

@Trey2k
Copy link
Member

Trey2k commented Sep 8, 2023

If you could test the CustomCallable PR (godotengine/godot#79005) with this language binding and report whether or not it was sufficient for your needs, that could help get it merged. :-) The main thing holding back that PR at the moment, is that we're waiting for other language bindings to try it out and give feedback.

Sure thing! Ill give it a test later this evening and report back.

@Trey2k
Copy link
Member

Trey2k commented Sep 8, 2023

@dsnopek I am having some trouble testing the PR, I maintain a fork of godot-cpp with godotengine/godot-cpp#1017 applied, without this PR LuaAPI will not compile at all. There is no workaround I can apply that I know of. This is another big one we have been waiting on for awhile. The issue I am having is there seems to be a lot of merge conflicts and blindly accepting incoming changes does not seem work.

I don't have that much time to dig through the actual issues at the moment sadly, i might give it some more time this weekend.

@RadiantUwU
Copy link
Contributor Author

@dsnopek I am having some trouble testing the PR, I maintain a fork of godot-cpp with godotengine/godot-cpp#1017 applied, without this PR LuaAPI will not compile at all. There is no workaround I can apply that I know of. This is another big one we have been waiting on for awhile. The issue I am having is there seems to be a lot of merge conflicts and blindly accepting incoming changes does not seem work.

I don't have that much time to dig through the actual issues at the moment sadly, i might give it some more time this weekend.

Wish i could fix the merge conflicts since i sure as hell dont have anything to do.

@Trey2k
Copy link
Member

Trey2k commented Sep 8, 2023

Wish i could fix the merge conflicts since i sure as hell dont have anything to do.

Feel free, my failed attempt is here https://github.com/Trey2k/godot-cpp/tree/CallableCustom

@dsnopek
Copy link

dsnopek commented Sep 8, 2023

I am having some trouble testing the PR, I maintain a fork of godot-cpp with godotengine/godot-cpp#1017 applied, without this PR LuaAPI will not compile at all.

PR godotengine/godot-cpp#1017 needs to get rebased and updated for the review I gave last month before it can be merged. The creator hasn't responded, so I've been thinking about taking that one over in a new PR, but haven't had a chance to do it yet. It is definitely an issue I'd like to see fixed!

@Trey2k
Copy link
Member

Trey2k commented Sep 9, 2023

@dsnopek Thank you for all the work you are doing. I have a small update for you, I have gotten godotengine/godot-cpp#1155 to merge with godotengine/godot-cpp#1238 which now builds godot-cpp without issue. I have also compiled Godot with godotengine/godot#79005 and extracted the GDExtension interface.

I am not sure if I am overlooking something or what, but no class for CallableCustom seems to generate to be able to extend from. Does this indicate something went wrong during the merge, or are we supposed to create CallableCustoms in a different way then within Godot itself? You can find the fork of godot-cpp I am working on here if it is any help https://github.com/Trey2k/godot-cpp/tree/CallableCustom

@dsnopek
Copy link

dsnopek commented Sep 10, 2023

Ah, so the Godot PR adds the API to gdextension_interface.h to create custom callables, but the godot-cpp PR only uses that API to implement callable_mp() and callable_mp_static(), it doesn't provide a base class for making your own custom callables. But that's a good idea! When I have a chance I think I'll try to extend the godot-cpp PR to include something like that.

However, in the meantime, you could use the APIs added to gdextension_interface.h: basically, you make a GDExtensionCallableCustomInfo struct and pass it to the godot::internal::gdextension_interface_callable_custom_create() function.

@Trey2k
Copy link
Member

Trey2k commented Sep 10, 2023

However, in the meantime, you could use the APIs added to gdextension_interface.h: basically, you make a GDExtensionCallableCustomInfo struct and pass it to the godot::internal::gdextension_interface_callable_custom_create() function.

Makes sense, I will attempt this evening and report back. Thank you!

@Trey2k
Copy link
Member

Trey2k commented Sep 14, 2023

I think my merge is bad, the test fail to build with

 -DNDEBUG -I/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/gdextension -I/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include -I/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/gen/include -I/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/src /home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/gen/src/classes/texture3drd.cpp
In file included from src/register_types.h:9,
                 from src/register_types.cpp:6:
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of 'static void godot::ClassDB::_register_class(bool) [with T = ExampleRef; bool is_abstract = false]':
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:212:36:   required from 'static void godot::ClassDB::register_class(bool) [with T = ExampleRef]'
src/register_types.cpp:24:37:   required from here
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:181:39: error: invalid conversion from 'void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool)' {aka 'void (*)(void*, int, unsigned char)'} to 'GDExtensionClassNotification2' {aka 'void (*)(void*, int, bool)'} [-fpermissive]
  181 |         GDExtensionClassCreationInfo2 class_info = {
      |                                       ^~~~~~~~~~
      |                                       |
      |                                       void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool) {aka void (*)(void*, int, unsigned char)}
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of 'static void godot::ClassDB::_register_class(bool) [with T = ExampleMin; bool is_abstract = false]':
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:212:36:   required from 'static void godot::ClassDB::register_class(bool) [with T = ExampleMin]'
src/register_types.cpp:25:37:   required from here
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:181:39: error: invalid conversion from 'void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool)' {aka 'void (*)(void*, int, unsigned char)'} to 'GDExtensionClassNotification2' {aka 'void (*)(void*, int, bool)'} [-fpermissive]
  181 |         GDExtensionClassCreationInfo2 class_info = {
      |                                       ^~~~~~~~~~
      |                                       |
      |                                       void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool) {aka void (*)(void*, int, unsigned char)}
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of 'static void godot::ClassDB::_register_class(bool) [with T = Example; bool is_abstract = false]':
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:212:36:   required from 'static void godot::ClassDB::register_class(bool) [with T = Example]'
src/register_types.cpp:26:34:   required from here
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:181:39: error: invalid conversion from 'void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool)' {aka 'void (*)(void*, int, unsigned char)'} to 'GDExtensionClassNotification2' {aka 'void (*)(void*, int, bool)'} [-fpermissive]
  181 |         GDExtensionClassCreationInfo2 class_info = {
      |                                       ^~~~~~~~~~
      |                                       |
      |                                       void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool) {aka void (*)(void*, int, unsigned char)}
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of 'static void godot::ClassDB::_register_class(bool) [with T = ExampleVirtual; bool is_abstract = false]':
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:212:36:   required from 'static void godot::ClassDB::register_class(bool) [with T = ExampleVirtual]'
src/register_types.cpp:27:41:   required from here
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:181:39: error: invalid conversion from 'void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool)' {aka 'void (*)(void*, int, unsigned char)'} to 'GDExtensionClassNotification2' {aka 'void (*)(void*, int, bool)'} [-fpermissive]
  181 |         GDExtensionClassCreationInfo2 class_info = {
      |                                       ^~~~~~~~~~
      |                                       |
      |                                       void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool) {aka void (*)(void*, int, unsigned char)}
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp: In instantiation of 'static void godot::ClassDB::_register_class(bool) [with T = ExampleAbstract; bool is_abstract = true]':
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:217:35:   required from 'static void godot::ClassDB::register_abstract_class() [with T = ExampleAbstract]'
src/register_types.cpp:28:51:   required from here
/home/trey2k/Development/WeaselGames/modules/luaAPI/external/godot-cpp/include/godot_cpp/core/class_db.hpp:181:39: error: invalid conversion from 'void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool)' {aka 'void (*)(void*, int, unsigned char)'} to 'GDExtensionClassNotification2' {aka 'void (*)(void*, int, bool)'} [-fpermissive]
  181 |         GDExtensionClassCreationInfo2 class_info = {
      |                                       ^~~~~~~~~~
      |                                       |
      |                                       void (*)(GDExtensionClassInstancePtr, int32_t, GDExtensionBool) {aka void (*)(void*, int, unsigned char)}
    

I don't know enough about the inner working of GDExtension yet to really debug this myself, but if I get more time I will try to dig into it.

@RadiantUwU
Copy link
Contributor Author

@Trey2k Seems like 2.1-stable will only be released for 4.2+

@RadiantUwU
Copy link
Contributor Author

godotengine/godot#79005 And this got merged as well, amazing

@Trey2k
Copy link
Member

Trey2k commented Sep 30, 2023

Seems like 2.1-stable will only be released for 4.2+

This will probably be the case unless the change is back ported yes.

@Trey2k
Copy link
Member

Trey2k commented Oct 8, 2023

Fixed for module based builds in #169

@Trey2k
Copy link
Member

Trey2k commented Oct 10, 2023

I wouldn't call this fully resolved until CallableCustoms actually work for GDExtension.

@dsnopek
Copy link

dsnopek commented Oct 22, 2023

Ah, so the Godot PR adds the API to gdextension_interface.h to create custom callables, but the godot-cpp PR only uses that API to implement callable_mp() and callable_mp_static(), it doesn't provide a base class for making your own custom callables. But that's a good idea! When I have a chance I think I'll try to extend the godot-cpp PR to include something like that.

Sorry it took me so long to circle back to this, but there is now a PR to godot-cpp that makes it easier to create your own custom callables (by extending the CallableCustom class):

godotengine/godot-cpp#1280

Please give it a try when you have a chance, and let me know if it works for you!

@michieal
Copy link
Contributor

I'm very hopeful for this, as I have encountered this bug myself, and wondered what had caused it.
It's especially noticeable when running your game in debug mode from your IDE instead of from the editor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants