Crash in MWPhysics::PhysicsTaskScheduler::removeCollisionObject
Happened with slightly modified 5f926bd1 but nothing physics related when player moves at high speed with 4 physics threads.
Crashed thread
Thread 15 (Thread 0x7310956006c0 (LWP 31822) "openmw"):
#0 0x00007310da50fec7 in wait4 () at /usr/lib/libc.so.6
#1 0x00005f4edd1a2bdc in crash_catcher (signum=11, siginfo=<optimized out>) at /home/elsid/dev/openmw/components/crashcatcher/crashcatcher.cpp:355
status = 0
fd = {57, 58}
dbg_pid = 32076
#2 0x00007310da45a770 in <signal handler called> () at /usr/lib/libc.so.6
#3 std::_Hashtable<btCollisionObject const*, btCollisionObject const*, std::allocator<btCollisionObject const*>, std::__detail::_Identity, std::equal_to<btCollisionObject const*>, std::hash<btCollisionObject const*>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_erase (__n=0x5f4af65007e2, __prev_n=0x5f4ef5252b00, __bkt=2425, this=0x5f4edf1f4ad0) at /usr/include/c++/13.2.1/bits/hashtable.h:2321
__result = {<std::__detail::_Node_iterator_base<btCollisionObject const*, false>> = {_M_cur = <optimized out>}, <No data fields>}
__result = {<std::__detail::_Node_iterator_base<btCollisionObject const*, false>> = {_M_cur = <optimized out>}, <No data fields>}
__next_bkt = <optimized out>
#4 std::_Hashtable<btCollisionObject const*, btCollisionObject const*, std::allocator<btCollisionObject const*>, std::__detail::_Identity, std::equal_to<btCollisionObject const*>, std::hash<btCollisionObject const*>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_erase (__k=<synthetic pointer>: <optimized out>, this=0x5f4edf1f4ad0) at /usr/include/c++/13.2.1/bits/hashtable.h:2381
__prev_n = 0x5f4ef5252b00
__n = 0x5f4af65007e2
__bkt = 2425
__prev_n = <optimized out>
__n = <optimized out>
__bkt = <optimized out>
__code = <optimized out>
#5 std::_Hashtable<btCollisionObject const*, btCollisionObject const*, std::allocator<btCollisionObject const*>, std::__detail::_Identity, std::equal_to<btCollisionObject const*>, std::hash<btCollisionObject const*>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::erase (__k=<synthetic pointer>: <optimized out>, this=0x5f4edf1f4ad0) at /usr/include/c++/13.2.1/bits/hashtable.h:984
#6 std::unordered_set<btCollisionObject const*, std::hash<btCollisionObject const*>, std::equal_to<btCollisionObject const*>, std::allocator<btCollisionObject const*> >::erase (__x=<synthetic pointer>: <optimized out>, this=0x5f4edf1f4ad0) at /usr/include/c++/13.2.1/bits/unordered_set.h:553
#7 MWPhysics::PhysicsTaskScheduler::removeCollisionObject (this=0x5f4edf1f4ac0, collisionObject=0x5f4f2d161290) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:660
lock = {mImpl = std::optional<class std::unique_lock<std::shared_mutex>> = {[contained value] = {_M_device = 0x5f4e00000000, _M_owns = false}}}
#8 0x00005f4edca3b92e in MWPhysics::Actor::~Actor (this=0x5f4f400e9e20, __in_chrg=<optimized out>) at /home/elsid/dev/openmw/apps/openmw/mwphysics/actor.cpp:117
#9 0x00005f4edbc1deaa in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use (this=0x5f4f400e9e10) at /usr/include/c++/13.2.1/bits/shared_ptr_base.h:175
#10 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use_cold (this=0x5f4f400e9e10) at /usr/include/c++/13.2.1/bits/shared_ptr_base.h:199
#11 0x00005f4edca492c0 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x5f4f400e9e10) at /usr/include/c++/13.2.1/bits/shared_ptr_base.h:353
__wordbits = 32
__shiftbits = 32
__unique_ref = 4294967297
__both_counts = <optimized out>
__lock_free = true
__double_word = true
__aligned = true
__lock_free = <optimized out>
__double_word = <optimized out>
__aligned = <optimized out>
__wordbits = <optimized out>
__shiftbits = <optimized out>
__unique_ref = <optimized out>
__both_counts = <optimized out>
#12 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/13.2.1/bits/shared_ptr_base.h:1071
#13 std::__shared_ptr<MWPhysics::Projectile, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/13.2.1/bits/shared_ptr_base.h:1524
#14 std::shared_ptr<MWPhysics::Projectile>::~shared_ptr (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/13.2.1/bits/shared_ptr.h:175
#15 std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >::~pair (this=<synthetic pointer>, __in_chrg=<optimized out>) at /usr/include/c++/13.2.1/bits/stl_pair.h:187
#16 (anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock>::operator()<MWPhysics::Projectile, MWPhysics::ProjectileFrameData> (sim=<optimized out>, this=<optimized out>) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:166
locked = Python Exception <class 'gdb.error'>: value has been optimized out
Python Exception <class 'gdb.error'>: value has been optimized out
{<std::_Optional_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, false, false>> = {<std::_Optional_base_impl<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, std::_Optional_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, false, false> >> = {<No data fields>}, _M_payload = {<std::_Optional_payload<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, true, false, false>> = {<std::_Optional_payload_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> > >> = {_M_payload = {_M_empty = {<No data fields>}, _M_value = {first = , second = {_M_data = <optimized out>}}}, _M_engaged = <optimized out>}, <No data fields>}, <No data fields>}}, <std::_Enable_copy_move<true, true, true, true, std::optional<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> > > >> = {<No data fields>}, <No data fields>}
frameData = <optimized out>
ptr = <optimized out>
arg = Python Exception <class 'gdb.error'>: value has been optimized out
{first = , second = {_M_data = <optimized out>}}
lock = {mImpl = std::variant<struct std::monostate, class std::unique_lock<std::shared_mutex>, class std::shared_lock<std::shared_mutex>> [index 2] = {{_M_pm = 0x5f4edf1f4bf8, _M_owns = true}}}
locked = Python Exception <class 'gdb.error'>: value has been optimized out
Python Exception <class 'gdb.error'>: value has been optimized out
{<std::_Optional_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, false, false>> = {<std::_Optional_base_impl<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, std::_Optional_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, false, false> >> = {<No data fields>}, _M_payload = {<std::_Optional_payload<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> >, true, false, false>> = {<std::_Optional_payload_base<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> > >> = {_M_payload = {_M_empty = {<No data fields>}, _M_value = {first = , second = {_M_data = <optimized out>}}}, _M_engaged = <optimized out>}, <No data fields>}, <No data fields>}}, <std::_Enable_copy_move<true, true, true, true, std::optional<std::pair<std::shared_ptr<MWPhysics::Projectile>, std::reference_wrapper<MWPhysics::ProjectileFrameData> > > >> = {<No data fields>}, <No data fields>}
ptr = <optimized out>
frameData = <optimized out>
arg = Python Exception <class 'gdb.error'>: value has been optimized out
{first = , second = {_M_data = <optimized out>}}
lock = Python Exception <class 'gdb.error'>: value has been optimized out
{mImpl = {<std::__detail::__variant::_Variant_base<std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {<std::__detail::__variant::_Move_assign_base<false, std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {<std::__detail::__variant::_Copy_assign_base<false, std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {<std::__detail::__variant::_Move_ctor_base<false, std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {<std::__detail::__variant::_Copy_ctor_base<false, std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {<std::__detail::__variant::_Variant_storage<false, std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> >> = {_M_u = {_M_first = {_M_storage = {<No data fields>}}, _M_rest = {_M_first = {{_M_empty = {<No data fields>}, _M_storage = {_M_device = <optimized out>, _M_owns = <optimized out>}}}, _M_rest = {_M_first = {{_M_empty = {<No data fields>}, _M_storage = {_M_pm = <optimized out>, _M_owns = <optimized out>}}}, _M_rest = {<No data fields>}}}}, _M_index = <optimized out>}, <No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, <No data fields>}, <std::_Enable_default_constructor<true, std::variant<std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> > >> = {<No data fields>}, <std::_Enable_copy_move<false, false, true, true, std::variant<std::monostate, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex> > >> = {<No data fields>}, <No data fields>}}
#17 std::__invoke_impl<void, (anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock> const&, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData>&> (__f=<optimized out>) at /usr/include/c++/13.2.1/bits/invoke.h:61
#18 std::__invoke<(anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock> const&, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData>&> (__fn=<optimized out>) at /usr/include/c++/13.2.1/bits/invoke.h:96
#19 std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)((anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock> const&, std::variant<MWPhysics::SimulationImpl<MWPhysics::Actor, MWPhysics::ActorFrameData>, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData> >&)>, std::integer_sequence<unsigned long, 1> >::__visit_invoke (__vars#0=<optimized out>, __visitor=<optimized out>) at /usr/include/c++/13.2.1/variant:1032
#20 std::__do_visit<std::__detail::__variant::__deduce_visit_result<void>, (anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock> const&, std::variant<MWPhysics::SimulationImpl<MWPhysics::Actor, MWPhysics::ActorFrameData>, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData> >&> (__visitor=<synthetic pointer>...) at /usr/include/c++/13.2.1/variant:1792
__v0 = std::variant<MWPhysics::SimulationImpl<MWPhysics::Actor, MWPhysics::ActorFrameData>, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData>> [index 0] = {{mPtr = std::weak_ptr<MWPhysics::Actor> (expired, weak count 2) = {get() = 0x5f4f400e9e20}, mData = {mPosition = {_v = {41098.1328, 63673.4609, 11085.9277}}, mInertia = {_v = {0, 0, 0}}, mStandingOn = 0x0, mIsOnGround = false, mIsOnSlope = false, mWalkingOnWater = false, mInert = false, mCollisionObject = 0x5f4f2d161290, mSwimLevel = -266.012878, mSlowFall = 1, mRotation = {_v = {0, 0}}, mMovement = {_v = {0.167000726, -2.86530304, 0}}, mLastStuckPosition = {_v = {0, 0, 0}}, mWaterlevel = -1, mHalfExtentsZ = 147.229385, mOldHeight = 11085.9277, mStuckFrames = 0, mFlying = true, mWasOnGround = true, mIsAquatic = false, mWaterCollision = false, mSkipCollisionDetection = false, mIsPlayer = false}}}
__max = 11
__n = 2
#21 std::visit<(anonymous namespace)::Visitors::WithLockedPtr<(anonymous namespace)::Visitors::Move, MWPhysics::(anonymous namespace)::MaybeLock> const&, std::variant<MWPhysics::SimulationImpl<MWPhysics::Actor, MWPhysics::ActorFrameData>, MWPhysics::SimulationImpl<MWPhysics::Projectile, MWPhysics::ProjectileFrameData> >&> (__visitor=<synthetic pointer>...) at /usr/include/c++/13.2.1/variant:1854
__visit_rettypes_match = true
__visit_rettypes_match = <optimized out>
#22 MWPhysics::PhysicsTaskScheduler::doSimulation (this=this@entry=0x5f4edf1f4ac0) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:787
job = <optimized out>
impl = {mPhysicsDt = 0.0197194088, mCollisionWorld = 0x5f4ee2556c80, mWorldFrameData = @0x5f4f28cdca00}
vis = {mImpl = <optimized out>, mCollisionWorldMutex = <optimized out>, mLockingPolicy = <optimized out>}
#23 0x00005f4edca496d6 in operator() (__closure=<optimized out>) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:748
lock = {_M_pm = <optimized out>, _M_owns = <optimized out>}
this = <optimized out>
this = <optimized out>
lock = {_M_pm = <optimized out>, _M_owns = <optimized out>}
#24 MWPhysics::PhysicsTaskScheduler::WorkersSync::runWorker<MWPhysics::PhysicsTaskScheduler::worker()::<lambda()> > (f=<optimized out>, this=0x5f4edd8574c0) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:384
lastFrame = <optimized out>
lock = {_M_device = 0x5f4edd857560, _M_owns = false}
lastFrame = <optimized out>
lock = {_M_device = <optimized out>, _M_owns = <optimized out>}
#25 MWPhysics::PhysicsTaskScheduler::worker (this=0x5f4edf1f4ac0) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:746
#26 operator() (__closure=<optimized out>) at /home/elsid/dev/openmw/apps/openmw/mwphysics/mtphysics.cpp:430
this = <optimized out>
this = <optimized out>
#27 std::__invoke_impl<void, MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::<lambda()> > (__f=<optimized out>) at /usr/include/c++/13.2.1/bits/invoke.h:61
#28 std::__invoke<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::<lambda()> > (__fn=<optimized out>) at /usr/include/c++/13.2.1/bits/invoke.h:96
#29 std::thread::_Invoker<std::tuple<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::<lambda()> > >::_M_invoke<0> (this=<optimized out>) at /usr/include/c++/13.2.1/bits/std_thread.h:292
#30 std::thread::_Invoker<std::tuple<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::<lambda()> > >::operator() (this=<optimized out>) at /usr/include/c++/13.2.1/bits/std_thread.h:299
#31 std::thread::_State_impl<std::thread::_Invoker<std::tuple<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::<lambda()> > > >::_M_run(void) (this=<optimized out>) at /usr/include/c++/13.2.1/bits/std_thread.h:244
#32 0x00007310da6e1943 in std::execute_native_thread_routine (__p=0x5f4ee1e65770) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:104
__t = std::unique_ptr<std::thread::_State> = {get() = <optimized out>}
#33 0x00007310da4a955a in ??? () at /usr/lib/libc.so.6
#34 0x00007310da526a5c in ??? () at /usr/lib/libc.so.6
Basically it happened on removing std::unordered_set
item here. For some reason this happens outside the lock. Maybe there is another one that could create a critical section but that's weird to have unique lock on top of lock created in this function. Also addCollisionObject is called from the main thread modifying the same container so this is clear UB.
!1048 (merged) adding the map is also a part of 0.48 so this is probably not a short term regression.
Edited by elsid