Skip to content

Conversation

@Ivorforce
Copy link
Member

@Ivorforce Ivorforce commented Nov 26, 2025

I'm not sure why I haven't thought of this problem when suggesting to use AHashMap for #1839, or noticed it on any of the issues that popped up. That was a blunder on my part. Sorry, folks!

The problem materializes (in potentially various semi-related ways) as soon as at least ~13 classes are registered.

Explanation

ClassDB::classes holds ClassInfo densely. That means that on every resize, the ClassInfo structs move in memory, which makes pointers to them illegal.
@SiimonDev correctly identified this as a source of failure, and in #1886 fixes some issues by removing pointers to the ClassInfo structs.
However, I think it is expected that the ClassInfo structs have stable locations in memory. Not being able to hold pointers to it could be a significant performance burden.

Stable memory locations could be accomplished by either using HashMap (see pointer stability in Core types), like Godot (https://github.com/godotengine/godot/blob/9dd6c4dbac70b28e8156255c3a2b78722772b036/core/object/class_db.h#L204), or by changing it to hold ClassInfo * (like the current state of godotengine/godot#106646).
I'm not fully convinced of the benefits of using AHashMap when we're using allocations with values anyway, which is why I'm hesitant to merge godotengine/godot#106646 right now. So that's why Godot upstream is still using HashMap.

Long story short, HashMap guarantees pointer stability, which should fix the problem.

@Ivorforce Ivorforce requested a review from a team as a code owner November 26, 2025 10:12
@Ivorforce Ivorforce added bug This has been identified as a bug crash labels Nov 26, 2025
@Ivorforce Ivorforce added this to the 4.6 milestone Nov 26, 2025
@Ivorforce Ivorforce force-pushed the classes-pointer-stability branch from 72e3e79 to c7ee662 Compare November 26, 2025 10:16
Copy link
Collaborator

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@SiimonDev
Copy link

Nice, thank you!

@Ivorforce
Copy link
Member Author

Ivorforce commented Nov 26, 2025

I wasn't able to reproduce a crash with 35 classes (I suspect memory keeps being allocated inside the same page as the previous AHashMap buffers), would probably need even more classes / memory churn in between registering to trigger it), but I was able to confirm that parent_ptr was incorrect after registering too many classes with the following:

	print_line("===========================");
	for (auto &info : classes) {
		print_line(info.value.name);
		print_line((uint64_t)&info.value);
		print_line("--" + String::num_int64((uint64_t)info.value.parent_ptr));
	}
	print_line("===========================");

(printing old / outdated pointers for parent_ptr that don't appear in the values themselves).
Cherry-picking 788edc6 (this PR) fixes this issue.

@SiimonDev
Copy link

Might be redundant but I tested this in my project where AHashMap would crash previously. This commit however ran without any issues. Thank you very much!

@dsnopek dsnopek merged commit 7f75888 into godotengine:master Nov 27, 2025
16 checks passed
@Ivorforce Ivorforce deleted the classes-pointer-stability branch November 27, 2025 11:22
@dsnopek
Copy link
Collaborator

dsnopek commented Nov 30, 2025

Cherry-picked for 4.4 in PR #1890

@dsnopek
Copy link
Collaborator

dsnopek commented Nov 30, 2025

Cherry-picked for 4.5 in PR #1891

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug This has been identified as a bug crash regression

Projects

None yet

3 participants