SanderMertens / flecs

A fast entity component system (ECS) for C & C++
https://www.flecs.dev
Other
6.47k stars 454 forks source link

`invalid constructor for flecs.monitor` crash #865

Closed Eshnek closed 2 years ago

Eshnek commented 2 years ago

Hello!

I recently started with flecs and like it a lot so far.

I encountered this assertion failure while building unit tests, and have made a small repro:

Repro

struct C_Something
{
    void* value {};
};

{
    flecs::world world {};

    world.entity ().set< C_Something > ( {} );
}
{
    flecs::world world {};

    world.import < flecs::monitor > ();
    // world.set< flecs::Rest > ( {} );

    world.entity ().set< C_Something > ( {} );
}

// fatal: lifecycle_traits.hpp: 8: invalid constructor for flecs.monitor (INVALID_OPERATION)

Stack Trace

win32u.dll!00007ffdda7015f4() (Unknown Source:0)
user32.dll!00007ffdda7316c6() (Unknown Source:0)
user32.dll!00007ffdda73152b() (Unknown Source:0)
user32.dll!00007ffdda7a7cf6() (Unknown Source:0)
user32.dll!00007ffdda7a6659() (Unknown Source:0)
user32.dll!00007ffdda7a7438() (Unknown Source:0)
user32.dll!00007ffdda7a74be() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc55367() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc6346d() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc63333() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc63547() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc644a7() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc65443() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc66edd() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc65330() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc52e51() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc53003() (Unknown Source:0)
ucrtbased.dll!00007ffd1cc6ab0d() (Unknown Source:0)
App.exe!flecs::_::ecs_ctor_illegal(void * __formal, int __formal, const ecs_type_info_t * ti) Line 8 (c:\Users\user\workspace\App\cxx\lib\flecs\include\flecs\addons\cpp\lifecycle_traits.hpp:8)
flecs.dll!flecs_table_grow_column(ecs_world_t * world, ecs_vec_t * column, ecs_type_info_t * ti, int to_add, int dst_size, bool construct) Line 1405 (c:\Users\user\workspace\App\cxx\lib\flecs\src\table.c:1405)
flecs.dll!flecs_table_append(ecs_world_t * world, ecs_table_t * table, unsigned __int64 entity, ecs_record_t * record, bool construct, bool on_add) Line 1569 (c:\Users\user\workspace\App\cxx\lib\flecs\src\table.c:1569)
flecs.dll!flecs_new_entity(ecs_world_t * world, unsigned __int64 entity, ecs_record_t * record, ecs_table_t * table, ecs_table_diff_t * diff, bool ctor, unsigned int evt_flags) Line 578 (c:\Users\user\workspace\App\cxx\lib\flecs\src\entity.c:578)
flecs.dll!flecs_commit(ecs_world_t * world, unsigned __int64 entity, ecs_record_t * record, ecs_table_t * dst_table, ecs_table_diff_t * diff, bool construct, unsigned int evt_flags) Line 749 (c:\Users\user\workspace\App\cxx\lib\flecs\src\entity.c:749)
flecs.dll!flecs_add_id_w_record(ecs_world_t * world, unsigned __int64 entity, ecs_record_t * record, unsigned __int64 id, bool construct) Line 893 (c:\Users\user\workspace\App\cxx\lib\flecs\src\entity.c:893)
flecs.dll!flecs_get_mut(ecs_world_t * world, unsigned __int64 entity, unsigned __int64 id, ecs_record_t * r) Line 971 (c:\Users\user\workspace\App\cxx\lib\flecs\src\entity.c:971)
flecs.dll!ecs_get_mut_id(ecs_world_t * world, unsigned __int64 entity, unsigned __int64 id) Line 2592 (c:\Users\user\workspace\App\cxx\lib\flecs\src\entity.c:2592)
App.exe!flecs::set<`CATCH2_INTERNAL_TEST_1043'::`2'::C_Something,0>(ecs_world_t * world, unsigned __int64 entity, CATCH2_INTERNAL_TEST_1043::__l2::C_Something && value, unsigned __int64 id) Line 12 (c:\Users\user\workspace\App\cxx\lib\flecs\include\flecs\addons\cpp\world.hpp:12)
App.exe!flecs::set<`CATCH2_INTERNAL_TEST_1043'::`2'::C_Something,`CATCH2_INTERNAL_TEST_1043'::`2'::C_Something>(ecs_world_t * world, unsigned __int64 entity, CATCH2_INTERNAL_TEST_1043::__l2::C_Something && value) Line 77 (c:\Users\user\workspace\App\cxx\lib\flecs\include\flecs\addons\cpp\world.hpp:77)
App.exe!flecs::entity_builder<flecs::entity>::set<`CATCH2_INTERNAL_TEST_1043'::`2'::C_Something,0>(CATCH2_INTERNAL_TEST_1043::__l2::C_Something && value) Line 611 (c:\Users\user\workspace\App\cxx\lib\flecs\include\flecs\addons\cpp\mixins\entity\builder.hpp:611)
App.exe!CATCH2_INTERNAL_TEST_1043() Line 24 (c:\Users\user\workspace\App\cxx\src\tests\groups\before_world\ecs_\basics\Cosmos.test.hxx:24)
App.exe!Catch::TestInvokerAsFunction::invoke() Line 150 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\internal\catch_test_case_registry_impl.cpp:150)
App.exe!Catch::TestCaseHandle::invoke() Line 115 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\catch_test_case_info.hpp:115)
App.exe!Catch::RunContext::invokeActiveTestCase() Line 503 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\internal\catch_run_context.cpp:503)
App.exe!Catch::RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) Line 475 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\internal\catch_run_context.cpp:475)
App.exe!Catch::RunContext::runTest(const Catch::TestCaseHandle & testCase) Line 239 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\internal\catch_run_context.cpp:239)
App.exe!Catch::`anonymous namespace'::TestGroup::execute() Line 112 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\catch_session.cpp:112)
App.exe!Catch::Session::runInternal() Line 337 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\catch_session.cpp:337)
App.exe!Catch::Session::run() Line 265 (c:\Users\user\workspace\App\cxx\lib\Catch2\src\catch2\catch_session.cpp:265)
App.exe!TestRunner::runConfiguredSession() Line 221 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:221)
App.exe!TestRunner::configureAndRunSession(std::vector<TestGroup,std::allocator<TestGroup>> && newRequiredTags) Line 197 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:197)
App.exe!TestRunner::runSession(std::vector<TestGroup,std::allocator<TestGroup>> && newRequiredTags) Line 186 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:186)
App.exe!TestRunner::runTagGroupDraw() Line 119 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:119)
App.exe!TestRunner::runTagGroup(std::vector<TestGroup,std::allocator<TestGroup>> && newGroupTags) Line 107 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:107)
App.exe!TestRunner::runExclusiveGroup(const TestGroup & group) Line 95 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:95)
App.exe!`TestRunner::runAllGroups'::`2'::<lambda_1>::operator()<TestGroup>(const TestGroup & group) Line 76 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:76)
App.exe!TestRunner::iterateExclusiveGroups<`TestRunner::runAllGroups'::`2'::<lambda_1>>(TestRunner::runAllGroups::__l2::<lambda_1> && functor) Line 85 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:85)
App.exe!TestRunner::runAllGroups() Line 78 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:78)
App.exe!TestRunner::TestRunner() Line 17 (c:\Users\user\workspace\App\cxx\src\tests\test\TestRunner.txx:17)
App.exe!TestDispatcher::tryDispatch() Line 39 (c:\Users\user\workspace\App\cxx\src\tests\test\TestDispatcher.txx:39)
App.exe!TestDispatcher::dispatch() Line 31 (c:\Users\user\workspace\App\cxx\src\tests\test\TestDispatcher.txx:31)
App.exe!TestDispatcher::TestDispatcher() Line 17 (c:\Users\user\workspace\App\cxx\src\tests\test\TestDispatcher.txx:17)
App.exe!DrawLoop::test() Line 30 (c:\Users\user\workspace\App\cxx\src\main\DrawLoop.cxx:30)
App.exe!DrawLoop::DrawLoop() Line 19 (c:\Users\user\workspace\App\cxx\src\main\DrawLoop.cxx:19)
App.exe!Engine::runGame() Line 59 (c:\Users\user\workspace\App\cxx\src\main\Engine.cxx:59)
App.exe!Engine::tryRunGame() Line 42 (c:\Users\user\workspace\App\cxx\src\main\Engine.cxx:42)
App.exe!Engine::run() Line 32 (c:\Users\user\workspace\App\cxx\src\main\Engine.cxx:32)
App.exe!main(int argc, char * * argv) Line 15 (c:\Users\user\workspace\App\cxx\src\Main.cxx:15)
App.exe!invoke_main() Line 79 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:79)
App.exe!__scrt_common_main_seh() Line 288 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
App.exe!__scrt_common_main() Line 331 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:331)
App.exe!mainCRTStartup(void * __formal) Line 17 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:17)
kernel32.dll!00007ffddbb7244d() (Unknown Source:0)
ntdll.dll!00007ffddc98dfb8() (Unknown Source:0)

I observe that when ti->hooks.on_add is retrieved the function pointer goes to the invalid ctor method, but I don't have a deep enough understanding to debug beyond that yet.

Or did I do something wrong?

SanderMertens commented 2 years ago

The reason this happens is because of the order in which components are registered in between the worlds. In the first world, C_Something is the first thing that gets registered. In the 2nd world, flecs::monitor is the first thing that gets registered, which as a result gets the same id as the old C_Something component.

Because component ids are cached in static global variables, the next time you try to use C_Something, it conflicts with the existing id for flecs::monitor. The error message is not super clear, I just checked in a fix that changes it to:

component 'C_Something' with symbol 'C_Something' already registered with symbol 'flecs.monitor' (INCONSISTENT_NAME)

The solution for this depends on what you're trying to do. If you have a multi-world application, you'll have to make sure that overlapping components are registered in the same order. If you have an application that creates and deletes worlds multiple times (like sometimes happens in tests), you can use this function to reset component ids:

flecs::reset();
Eshnek commented 2 years ago

Thank you for the explanation, that makes sense.