Skip to content

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.

openmw-crash.1714136195.log

!1048 (merged) adding the map is also a part of 0.48 so this is probably not a short term regression.

Edited by elsid