Data race in osg ParticleSystem (triggered when jumping into water)
Log:
WARNING: ThreadSanitizer: data race (pid=7927)
Write of size 8 at 0x7b6000020160 by main thread:
#0 void std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> >::_M_realloc_insert<osgParticle::Particle const&>(__gnu_cxx::__normal_iterator<osgParticle::Particle*, std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> > >, osgParticle::Particle const&) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/vector.tcc:452 (libosgParticle.so.160+0x0000000be0b0)
#1 std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> >::push_back(osgParticle::Particle const&) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/stl_vector.h:948 (libosgParticle.so.160+0x0000000bb7ad)
#2 osgParticle::ParticleSystem::createParticle(osgParticle::Particle const*) /home/xyz/osg/src/osgParticle/ParticleSystem.cpp:128 (libosgParticle.so.160+0x0000000b2b43)
#3 MWRender::RippleSimulation::emitRipple(osg::Vec3f const&) /home/xyz/openmw/apps/openmw/mwrender/ripplesimulation.cpp:199 (openmw+0x000000e7c2a5)
#4 MWRender::RippleSimulation::update(float) /home/xyz/openmw/apps/openmw/mwrender/ripplesimulation.cpp:142 (openmw+0x000000e7bc9b)
#5 MWRender::Water::update(float) /home/xyz/openmw/apps/openmw/mwrender/water.cpp:657 (openmw+0x000000e6bded)
#6 MWRender::RenderingManager::update(float, bool) /home/xyz/openmw/apps/openmw/mwrender/renderingmanager.cpp:599 (openmw+0x000000d77fb6)
#7 MWWorld::Scene::update(float, bool) /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:327 (openmw+0x00000127c6ef)
#8 MWWorld::World::update(float, bool) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1795 (openmw+0x00000121f609)
#9 OMW::Engine::frame(float) /home/xyz/openmw/apps/openmw/engine.cpp:162 (openmw+0x0000016699f4)
#10 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:716 (openmw+0x000001670078)
#11 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x00000164e5a2)
#12 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:135 (openmw+0x00000186e9b3)
#13 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000164e69b)
Previous read of size 8 at 0x7b6000020160 by thread T1 (mutexes: write M43482437885932392):
#0 std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> >::size() const /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/stl_vector.h:671 (openmw+0x000000de5b28)
#1 osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo&) const /home/xyz/osg/src/osgParticle/ParticleSystem.cpp:222 (libosgParticle.so.160+0x0000000b3358)
#2 osg::Drawable::drawInner(osg::RenderInfo&) const /home/xyz/osg/include/osg/Drawable:276 (libosg.so.160+0x0000004842be)
#3 osg::Drawable::draw(osg::RenderInfo&) const /home/xyz/osg/src/osg/Drawable.cpp:688 (libosg.so.160+0x000000483449)
#4 osgUtil::RenderLeaf::render(osg::RenderInfo&, osgUtil::RenderLeaf*) /home/xyz/osg/src/osgUtil/RenderLeaf.cpp:64 (libosgUtil.so.160+0x000000586460)
#5 osgUtil::RenderBin::drawImplementation(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderBin.cpp:467 (libosgUtil.so.160+0x000000572c0c)
#6 osgUtil::RenderBin::draw(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderBin.cpp:430 (libosgUtil.so.160+0x00000057295b)
#7 osgUtil::RenderBin::drawImplementation(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderBin.cpp:517 (libosgUtil.so.160+0x000000572f7a)
#8 osgUtil::RenderStage::drawImplementation(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderStage.cpp:1406 (libosgUtil.so.160+0x00000058e349)
#9 osgUtil::RenderBin::draw(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderBin.cpp:430 (libosgUtil.so.160+0x00000057295b)
#10 osgUtil::RenderStage::drawInner(osg::RenderInfo&, osgUtil::RenderLeaf*&, bool&) /home/xyz/osg/src/osgUtil/RenderStage.cpp:931 (libosgUtil.so.160+0x00000058c309)
#11 osgUtil::RenderStage::draw(osg::RenderInfo&, osgUtil::RenderLeaf*&) /home/xyz/osg/src/osgUtil/RenderStage.cpp:1242 (libosgUtil.so.160+0x00000058d967)
#12 osgUtil::SceneView::draw() /home/xyz/osg/src/osgUtil/SceneView.cpp:1367 (libosgUtil.so.160+0x0000005a71b4)
#13 osgViewer::Renderer::draw() /home/xyz/osg/src/osgViewer/Renderer.cpp:774 (libosgViewer.so.160+0x00000019c018)
#14 osgViewer::Renderer::operator()(osg::GraphicsContext*) /home/xyz/osg/src/osgViewer/Renderer.cpp:929 (libosgViewer.so.160+0x00000019d458)
#15 osg::GraphicsContext::runOperations() /home/xyz/osg/src/osg/GraphicsContext.cpp:696 (libosg.so.160+0x0000004f76b1)
#16 osg::RunOperations::operator()(osg::GraphicsContext*) /home/xyz/osg/src/osg/GraphicsThread.cpp:139 (libosg.so.160+0x0000005073ae)
#17 osg::GraphicsOperation::operator()(osg::Object*) /home/xyz/osg/src/osg/GraphicsThread.cpp:53 (libosg.so.160+0x0000005068c6)
#18 osg::OperationThread::run() /home/xyz/osg/src/osg/OperationThread.cpp:438 (libosg.so.160+0x0000005b4b05)
#19 osg::GraphicsThread::run() /home/xyz/osg/src/osg/GraphicsThread.cpp:38 (libosg.so.160+0x0000005067ea)
#20 OpenThreads::ThreadPrivateActions::StartThread(void*) <null> (libOpenThreads.so.21+0x00000000a2ba)
#21 <null> <null> (libtsan.so.0+0x00000002583b)
Location is heap block of size 920 at 0x7b6000020000 allocated by main thread:
#0 operator new(unsigned long) <null> (libtsan.so.0+0x00000006f546)
#1 MWRender::RippleSimulation::RippleSimulation(osg::Group*, Resource::ResourceSystem*, Fallback::Map const*) /home/xyz/openmw/apps/openmw/mwrender/ripplesimulation.cpp:87 (openmw+0x000000e7b1fa)
#2 MWRender::Water::Water(osg::Group*, osg::Group*, Resource::ResourceSystem*, osgUtil::IncrementalCompileOperation*, Fallback::Map const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/apps/openmw/mwrender/water.cpp:404 (openmw+0x000000e69349)
#3 MWRender::RenderingManager::RenderingManager(osgViewer::Viewer*, osg::ref_ptr<osg::Group>, Resource::ResourceSystem*, SceneUtil::WorkQueue*, Fallback::Map const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, DetourNavigator::Navigator&) /home/xyz/openmw/apps/openmw/mwrender/renderingmanager.cpp:254 (openmw+0x000000d73a8c)
#4 MWWorld::World::World(osgViewer::Viewer*, osg::ref_ptr<osg::Group>, Resource::ResourceSystem*, SceneUtil::WorkQueue*, Files::Collections const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, ToUTF8::Utf8Encoder*, std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:236 (openmw+0x000001210bf5)
#5 OMW::Engine::prepareEngine(Settings::Manager&) /home/xyz/openmw/apps/openmw/engine.cpp:535 (openmw+0x00000166e3a1)
#6 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:663 (openmw+0x00000166f5dc)
#7 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x00000164e5a2)
#8 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:135 (openmw+0x00000186e9b3)
#9 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000164e69b)
Mutex M43482437885932392 is already destroyed.
Thread T1 (tid=8022, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.0+0x000000028e53)
#1 OpenThreads::Thread::start() /home/xyz/osg/src/OpenThreads/pthreads/PThread.cpp:671 (libOpenThreads.so.21+0x000000008e1c)
#2 OpenThreads::Thread::startThread() /home/xyz/osg/src/OpenThreads/pthreads/PThread.cpp:694 (libOpenThreads.so.21+0x000000008ec9)
#3 osgViewer::ViewerBase::startThreading() /home/xyz/osg/src/osgViewer/ViewerBase.cpp:600 (libosgViewer.so.160+0x000000208354)
#4 osgViewer::ViewerBase::setUpThreading() /home/xyz/osg/src/osgViewer/ViewerBase.cpp:302 (libosgViewer.so.160+0x000000206bab)
#5 osgViewer::Viewer::realize() /home/xyz/osg/src/osgViewer/Viewer.cpp:612 (libosgViewer.so.160+0x0000001fc57b)
#6 OMW::Engine::createWindow(Settings::Manager&) /home/xyz/openmw/apps/openmw/engine.cpp:414 (openmw+0x00000166c59f)
#7 OMW::Engine::prepareEngine(Settings::Manager&) /home/xyz/openmw/apps/openmw/engine.cpp:448 (openmw+0x00000166d129)
#8 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:663 (openmw+0x00000166f5dc)
#9 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x00000164e5a2)
#10 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:135 (openmw+0x00000186e9b3)
#11 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000164e69b)
SUMMARY: ThreadSanitizer: data race /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/vector.tcc:452 in void std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> >::_M_realloc_insert<osgParticle::Particle const&>(__gnu_cxx::__normal_iterator<osgParticle::Particle*, std::vector<osgParticle::Particle, std::allocator<osgParticle::Particle> > >, osgParticle::Particle const&)
This looks like a bug in OSG to me, but maybe it's just OpenMW doing something with OSG it shouldn't? I couldn't find any thread safety guarantees in OSG docs.
So what's happening is OpenMW is inserting a particle using createParticle
and the function is writing to the _particles
vector here: https://github.com/OpenMW/osg/blob/ecedf3232c2f1b3b6a2f06f77ea37a9afb6a93f5/src/osgParticle/ParticleSystem.cpp#L119
Meanwhile, the render thread is accessing the same _particles
in drawImplementation
here https://github.com/OpenMW/osg/blob/ecedf3232c2f1b3b6a2f06f77ea37a9afb6a93f5/src/osgParticle/ParticleSystem.cpp#L222
There's an "rw" lock but it's only used in drawImplementation
and not in createParticle
.
Adding a ScopedWriteLock lock(_readWriteMutex);
as the first line in osgParticle::ParticleSystem::createParticle
should resolve the race but again I'm not sure if the problem's OpenMW using OSG wrong or it's a legitimate bug in OSG.