godot-cpp: Crash on shutdown if certain singletons were used in extension

When using certain singletons like OS::get_singleton() from within a GDExtension it seems like internal::gdn_interface->object_get_instance_binding in the generated get_singleton() ends up storing callbacks on the editor side to OS::___binding_callbacks which point to functions on the extension side.

These callbacks (___binding_free_callback specifically) end up getting invoked on editor shutdown (at Object::~Object()) during memdelete(_os) in unregister_core_types(). The problem is that by then we’ve already unloaded the extension libraries in memdelete(native_extension_manager), resulting in trying to call a function that no longer exists, leading to an access violation crash.

This also applies to Engine::get_singleton(). More singletons might be affected that I haven’t run into yet.

Editor: v4.0.beta.custom_build (ca25c6e0a) Architecture: x86_64 Compiler: MSVC (v19.33.31630) OS: Windows 11 (v10.0.22621)

Minimal repro:

#include <godot/gdnative_interface.h>

#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>

using namespace godot;

void initialize_example_module(ModuleInitializationLevel p_level) {
    if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
        OS::get_singleton();
    }
}

extern "C" {

GDNativeBool GDN_EXPORT example_library_init(
    const GDNativeInterface* p_native_iface,
    const GDNativeExtensionClassLibraryPtr p_native_lib,
    GDNativeInitialization* p_native_init
) {
    GDExtensionBinding::InitObject init_obj(p_native_iface, p_native_lib, p_native_init);
    init_obj.register_initializer(initialize_example_module);
    init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
    return init_obj.init();
}

} // extern "C"

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 1
  • Comments: 16 (16 by maintainers)

Most upvoted comments

Ah, ok, thanks!

Reading through the comments on your linked PR, Zylann brings up an important point that this issue probably affects all objects, not just singletons.

Hm, it’s starting to sound like this is a bigger, trickier problem to solve than it seemed at first! When I have the time, I’ll dig into it deeper.

That SingletonBinder in EffekseerForGodot4 assumes that all referenced singleton bindings call ___binding_create_callback at some point, which is not the case, and will double-free if the extension does something like expose a custom physics server, where the “binding” is effectively created by the extension itself.

I’ve been running a somewhat similar hack/fix for the past couple of months, which does account for this, and seems to work as far as I know. I’m not proposing that this be merged upstream though, as I’m sure there are more esoteric interactions where this too will break.

Someone with a much deeper understanding of the godot-cpp bindings likely needs to take a closer look at the whole binding setup in godot-cpp.

I’d appreciate it if this was resolved sooner rather than later. While crashes on exit will likely remain unnoticed by end users, it affects development process greatly, as you basically cannot exit your application or the editor normally, if you have a debugger attached.

If you just need a short-term fix for your own use, and you don’t mind forking/patching godot-cpp, my workaround has been working well in Godot Jolt for a good long while now.

I haven’t merged with upstream since 4.2-stable was released though, so it could be that it’s due for another touch-up, but so far they’ve all been quick and painless.

Yes, sorry, I meant that I’m surprised this issue isn’t flooded with people facing the same problem, not that I’m surprised that it hasn’t been fixed yet. It’s a tricky one.

I just feel like every non-trivial extension should run into this crash sooner or later.

I’m surprised this issue hasn’t gotten more traction than it has. I guess people just don’t run their editor/game through a debugger or something?

Well, unfortunately, I don’t think we know how to solve it yet. I don’t remember the details, but the last time we discussed this, it was shown that this issue affects more than just singletons, but singletons are the the easiest way to trigger it.

If anyone has a proposed solution, that’d be great! Otherwise, I’ll personally look at it eventually - there’s just so many things 😃

I’m not sure if OP suggests this only affects the editor, but I actually experience the crash when running the project.

I can’t remember if I’d only seen it in the context of the editor at that time, but yes, it most definitely happens when shutting down the game/application as well.

I know at least one other published GDExtension (Debug Draw 3D) that suffers from this issue, causing a crash on shutdown every single time for anyone who has it loaded. I’m sure there are other ones out there as well.

I’m surprised this issue hasn’t gotten more traction than it has. I guess people just don’t run their editor/game through a debugger or something?