mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-26 06:40:22 +00:00
To fix ASAN warning: =11799==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c000436058 at pc 0x55c90acccaa8 bp 0x7f787eeac830 sp 0x7f787eeac820 READ of size 8 at 0x60c000436058 thread T18 #0 0x55c90acccaa7 in std::operator==(std::_Deque_iterator<std::_List_iterator<DetourNavigator::Job>, std::_List_iterator<DetourNavigator::Job>&, std::_List_iterator<DetourNavigator::Job>*> const&, std::_Deque_iterator<std::_List_iterator<DetourNavigator::Job>, std::_List_iterator<DetourNavigator::Job>&, std::_List_iterator<DetourNavigator::Job>*> const&) /usr/include/c++/11.1.0/bits/stl_deque.h:269 #1 0x55c90acccaa7 in std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > >::empty() const /usr/include/c++/11.1.0/bits/stl_deque.h:1311 #2 0x55c90acccaa7 in operator() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:350 #3 0x55c90acccaa7 in wait_until<std::chrono::_V2::steady_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> >, DetourNavigator::AsyncNavMeshUpdater::getNextJob()::<lambda()> > /usr/include/c++/11.1.0/condition_variable:151 #4 0x55c90acccaa7 in wait_for<long int, std::ratio<1, 1000>, DetourNavigator::AsyncNavMeshUpdater::getNextJob()::<lambda()> > /usr/include/c++/11.1.0/condition_variable:175 #5 0x55c90acccaa7 in DetourNavigator::AsyncNavMeshUpdater::getNextJob() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:353 #6 0x55c90accdb2d in DetourNavigator::AsyncNavMeshUpdater::process() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:257 #7 0x55c90acce464 in operator() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:98 #8 0x55c90acce464 in __invoke_impl<void, DetourNavigator::AsyncNavMeshUpdater::AsyncNavMeshUpdater(const DetourNavigator::Settings&, DetourNavigator::TileCachedRecastMeshManager&, DetourNavigator::OffMeshConnectionsManager&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:61 #9 0x55c90acce464 in __invoke<DetourNavigator::AsyncNavMeshUpdater::AsyncNavMeshUpdater(const DetourNavigator::Settings&, DetourNavigator::TileCachedRecastMeshManager&, DetourNavigator::OffMeshConnectionsManager&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:96 #10 0x55c90acce464 in _M_invoke<0> /usr/include/c++/11.1.0/bits/std_thread.h:253 #11 0x55c90acce464 in operator() /usr/include/c++/11.1.0/bits/std_thread.h:260 #12 0x55c90acce464 in _M_run /usr/include/c++/11.1.0/bits/std_thread.h:211 #13 0x7f78c38fd3c3 in execute_native_thread_routine /build/gcc/src/gcc/libstdc++-v3/src/c++11/thread.cc:82 #14 0x7f78c36b1258 in start_thread (/usr/lib/libpthread.so.0+0x9258) #15 0x7f78c35da5e2 in __GI___clone (/usr/lib/libc.so.6+0xfe5e2) 0x60c000436058 is located 88 bytes inside of 120-byte region [0x60c000436000,0x60c000436078) freed by thread T0 here: #0 0x7f78c688cd69 in operator delete(void*, unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cpp:172 #1 0x55c90acdbe20 in __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::deallocate(std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >*, unsigned long) /usr/include/c++/11.1.0/ext/new_allocator.h:139 #2 0x55c90acdbe20 in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > > >::deallocate(std::allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >&, std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >*, unsigned long) /usr/include/c++/11.1.0/bits/alloc_traits.h:492 #3 0x55c90acdbe20 in std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_put_node(std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >*) /usr/include/c++/11.1.0/bits/stl_tree.h:565 #4 0x55c90acdbe20 in std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_drop_node(std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >*) /usr/include/c++/11.1.0/bits/stl_tree.h:632 #5 0x55c90acdbe20 in std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_erase(std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >*) /usr/include/c++/11.1.0/bits/stl_tree.h:1889 #6 0x55c90acc0569 in std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::clear() /usr/include/c++/11.1.0/bits/stl_tree.h:1254 #7 0x55c90acc0569 in std::map<std:🧵:id, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::clear() /usr/include/c++/11.1.0/bits/stl_map.h:1134 #8 0x55c90acc0569 in DetourNavigator::AsyncNavMeshUpdater::~AsyncNavMeshUpdater() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:105 #9 0x55c90acab2dc in DetourNavigator::NavigatorImpl::~NavigatorImpl() (/home/elsid/dev/openmw/build/gcc/asan/openmw+0x2d152dc) #10 0x55c9097db4b5 in std::default_delete<DetourNavigator::Navigator>::operator()(DetourNavigator::Navigator*) const /usr/include/c++/11.1.0/bits/unique_ptr.h:85 #11 0x55c9097db4b5 in std::unique_ptr<DetourNavigator::Navigator, std::default_delete<DetourNavigator::Navigator> >::~unique_ptr() /usr/include/c++/11.1.0/bits/unique_ptr.h:361 #12 0x55c9097db4b5 in MWWorld::World::~World() /home/elsid/dev/openmw/apps/openmw/mwworld/worldimp.cpp:534 #13 0x55c9097dc3a4 in MWWorld::World::~World() /home/elsid/dev/openmw/apps/openmw/mwworld/worldimp.cpp:534 #14 0x55c90a1925e4 in MWBase::Environment::cleanup() /home/elsid/dev/openmw/apps/openmw/mwbase/environment.cpp:192 #15 0x55c90a1f544d in OMW::Engine::~Engine() /home/elsid/dev/openmw/apps/openmw/engine.cpp:434 #16 0x55c90a1f6700 in OMW::Engine::~Engine() /home/elsid/dev/openmw/apps/openmw/engine.cpp:455 #17 0x55c90a19d523 in std::default_delete<OMW::Engine>::operator()(OMW::Engine*) const /usr/include/c++/11.1.0/bits/unique_ptr.h:85 #18 0x55c90a19d523 in std::unique_ptr<OMW::Engine, std::default_delete<OMW::Engine> >::~unique_ptr() /usr/include/c++/11.1.0/bits/unique_ptr.h:361 #19 0x55c90a19d523 in runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:320 #20 0x55c90a955634 in wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/elsid/dev/openmw/components/debug/debugging.cpp:205 #21 0x55c90a193be0 in main /home/elsid/dev/openmw/apps/openmw/main.cpp:328 #22 0x7f78c3503b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24) previously allocated by thread T18 here: #0 0x7f78c688bca1 in operator new(unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cpp:99 #1 0x55c90ace8c73 in __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::allocate(unsigned long, void const*) /usr/include/c++/11.1.0/ext/new_allocator.h:121 #2 0x55c90ace8c73 in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > > >::allocate(std::allocator<std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >&, unsigned long) /usr/include/c++/11.1.0/bits/alloc_traits.h:460 #3 0x55c90ace8c73 in std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_get_node() /usr/include/c++/11.1.0/bits/stl_tree.h:561 #4 0x55c90ace8c73 in std::_Rb_tree_node<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >* std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_create_node<std::piecewise_construct_t const&, std::tuple<std:🧵:id const&>, std::tuple<> >(std::piecewise_construct_t const&, std::tuple<std:🧵:id const&>&&, std::tuple<>&&) /usr/include/c++/11.1.0/bits/stl_tree.h:611 #5 0x55c90ace8c73 in std::_Rb_tree_iterator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > std::_Rb_tree<std:🧵:id, std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > >, std::_Select1st<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<std:🧵:id const&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > >, std::piecewise_construct_t const&, std::tuple<std:🧵:id const&>&&, std::tuple<>&&) /usr/include/c++/11.1.0/bits/stl_tree.h:2429 #6 0x55c90accc5fb in std::map<std:🧵:id, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > >, std::less<std:🧵:id>, std::allocator<std::pair<std:🧵:id const, std::deque<std::_List_iterator<DetourNavigator::Job>, std::allocator<std::_List_iterator<DetourNavigator::Job> > > > > >::operator[](std:🧵:id const&) /usr/include/c++/11.1.0/bits/stl_map.h:501 #7 0x55c90accc5fb in DetourNavigator::AsyncNavMeshUpdater::getNextJob() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:344 #8 0x55c90accdb2d in DetourNavigator::AsyncNavMeshUpdater::process() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:257 #9 0x55c90acce464 in operator() /home/elsid/dev/openmw/components/detournavigator/asyncnavmeshupdater.cpp:98 #10 0x55c90acce464 in __invoke_impl<void, DetourNavigator::AsyncNavMeshUpdater::AsyncNavMeshUpdater(const DetourNavigator::Settings&, DetourNavigator::TileCachedRecastMeshManager&, DetourNavigator::OffMeshConnectionsManager&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:61 #11 0x55c90acce464 in __invoke<DetourNavigator::AsyncNavMeshUpdater::AsyncNavMeshUpdater(const DetourNavigator::Settings&, DetourNavigator::TileCachedRecastMeshManager&, DetourNavigator::OffMeshConnectionsManager&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:96 #12 0x55c90acce464 in _M_invoke<0> /usr/include/c++/11.1.0/bits/std_thread.h:253 #13 0x55c90acce464 in operator() /usr/include/c++/11.1.0/bits/std_thread.h:260 #14 0x55c90acce464 in _M_run /usr/include/c++/11.1.0/bits/std_thread.h:211 #15 0x7f78c38fd3c3 in execute_native_thread_routine /build/gcc/src/gcc/libstdc++-v3/src/c++11/thread.cc:82 Thread T18 created by T0 here: #0 0x7f78c682bfa7 in __interceptor_pthread_create /build/gcc/src/gcc/libsanitizer/asan/asan_interceptors.cpp:216 #1 0x7f78c38fd6aa in std:🧵:_M_start_thread(std::unique_ptr<std:🧵:_State, std::default_delete<std:🧵:_State> >, void (*)()) /build/gcc/src/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663 #2 0x55c90ae008d1 in DetourNavigator::NavMeshManager::NavMeshManager(DetourNavigator::Settings const&) /home/elsid/dev/openmw/components/detournavigator/navmeshmanager.cpp:47 #3 0x55c90aca3bfe in DetourNavigator::NavigatorImpl::NavigatorImpl(DetourNavigator::Settings const&) /home/elsid/dev/openmw/components/detournavigator/navigatorimpl.cpp:13 #4 0x55c9097d9e6f in 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&, 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*, 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/elsid/dev/openmw/apps/openmw/mwworld/worldimp.cpp:196 #5 0x55c90a1e9992 in OMW::Engine::prepareEngine(Settings::Manager&) /home/elsid/dev/openmw/apps/openmw/engine.cpp:789 #6 0x55c90a1ed138 in OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:949 #7 0x55c90a19d4cb in runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:316 #8 0x55c90a955634 in wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/elsid/dev/openmw/components/debug/debugging.cpp:205 #9 0x55c90a193be0 in main /home/elsid/dev/openmw/apps/openmw/main.cpp:328 #10 0x7f78c3503b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24) SUMMARY: AddressSanitizer: heap-use-after-free /usr/include/c++/11.1.0/bits/stl_deque.h:269 in std::operator==(std::_Deque_iterator<std::_List_iterator<DetourNavigator::Job>, std::_List_iterator<DetourNavigator::Job>&, std::_List_iterator<DetourNavigator::Job>*> const&, std::_Deque_iterator<std::_List_iterator<DetourNavigator::Job>, std::_List_iterator<DetourNavigator::Job>&, std::_List_iterator<DetourNavigator::Job>*> const&) Shadow bytes around the buggy address: 0x0c188007ebb0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x0c188007ebc0: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa 0x0c188007ebd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c188007ebe0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x0c188007ebf0: 00 00 00 00 00 00 07 fa fa fa fa fa fa fa fa fa =>0x0c188007ec00: fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd fa 0x0c188007ec10: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x0c188007ec20: 00 00 00 00 00 00 02 fa fa fa fa fa fa fa fa fa 0x0c188007ec30: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x0c188007ec40: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x0c188007ec50: 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==11799==ABORTING
505 lines
18 KiB
C++
505 lines
18 KiB
C++
#include "asyncnavmeshupdater.hpp"
|
|
#include "debug.hpp"
|
|
#include "makenavmesh.hpp"
|
|
#include "settings.hpp"
|
|
#include "version.hpp"
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
#include <components/misc/thread.hpp>
|
|
#include <components/loadinglistener/loadinglistener.hpp>
|
|
|
|
#include <osg/Stats>
|
|
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
#include <set>
|
|
|
|
namespace
|
|
{
|
|
using DetourNavigator::ChangeType;
|
|
using DetourNavigator::TilePosition;
|
|
using DetourNavigator::UpdateType;
|
|
using DetourNavigator::ChangeType;
|
|
using DetourNavigator::Job;
|
|
using DetourNavigator::JobIt;
|
|
|
|
int getManhattanDistance(const TilePosition& lhs, const TilePosition& rhs)
|
|
{
|
|
return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y());
|
|
}
|
|
|
|
int getMinDistanceTo(const TilePosition& position, int maxDistance,
|
|
const std::set<std::tuple<osg::Vec3f, TilePosition>>& pushedTiles,
|
|
const std::set<std::tuple<osg::Vec3f, TilePosition>>& presentTiles)
|
|
{
|
|
int result = maxDistance;
|
|
for (const auto& [halfExtents, tile] : pushedTiles)
|
|
if (presentTiles.find(std::tie(halfExtents, tile)) == presentTiles.end())
|
|
result = std::min(result, getManhattanDistance(position, tile));
|
|
return result;
|
|
}
|
|
|
|
UpdateType getUpdateType(ChangeType changeType) noexcept
|
|
{
|
|
if (changeType == ChangeType::update)
|
|
return UpdateType::Temporary;
|
|
return UpdateType::Persistent;
|
|
}
|
|
|
|
auto getPriority(const Job& job) noexcept
|
|
{
|
|
return std::make_tuple(job.mProcessTime, job.mTryNumber, job.mChangeType, job.mDistanceToPlayer, job.mDistanceToOrigin);
|
|
}
|
|
|
|
struct LessByJobPriority
|
|
{
|
|
bool operator()(JobIt lhs, JobIt rhs) const noexcept
|
|
{
|
|
return getPriority(*lhs) < getPriority(*rhs);
|
|
}
|
|
};
|
|
|
|
void insertPrioritizedJob(JobIt job, std::deque<JobIt>& queue)
|
|
{
|
|
const auto it = std::upper_bound(queue.begin(), queue.end(), job, LessByJobPriority {});
|
|
queue.insert(it, job);
|
|
}
|
|
|
|
auto getAgentAndTile(const Job& job) noexcept
|
|
{
|
|
return std::make_tuple(job.mAgentHalfExtents, job.mChangedTile);
|
|
}
|
|
}
|
|
|
|
namespace DetourNavigator
|
|
{
|
|
Job::Job(const osg::Vec3f& agentHalfExtents, std::weak_ptr<GuardedNavMeshCacheItem> navMeshCacheItem,
|
|
const TilePosition& changedTile, ChangeType changeType, int distanceToPlayer,
|
|
std::chrono::steady_clock::time_point processTime)
|
|
: mAgentHalfExtents(agentHalfExtents)
|
|
, mNavMeshCacheItem(std::move(navMeshCacheItem))
|
|
, mChangedTile(changedTile)
|
|
, mProcessTime(processTime)
|
|
, mChangeType(changeType)
|
|
, mDistanceToPlayer(distanceToPlayer)
|
|
, mDistanceToOrigin(getManhattanDistance(changedTile, TilePosition {0, 0}))
|
|
{
|
|
}
|
|
|
|
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager,
|
|
OffMeshConnectionsManager& offMeshConnectionsManager)
|
|
: mSettings(settings)
|
|
, mRecastMeshManager(recastMeshManager)
|
|
, mOffMeshConnectionsManager(offMeshConnectionsManager)
|
|
, mShouldStop()
|
|
, mNavMeshTilesCache(settings.mMaxNavMeshTilesCacheSize)
|
|
{
|
|
for (std::size_t i = 0; i < mSettings.get().mAsyncNavMeshUpdaterThreads; ++i)
|
|
mThreads.emplace_back([&] { process(); });
|
|
}
|
|
|
|
AsyncNavMeshUpdater::~AsyncNavMeshUpdater()
|
|
{
|
|
mShouldStop = true;
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
mThreadsQueues.clear();
|
|
mWaiting.clear();
|
|
mHasJob.notify_all();
|
|
lock.unlock();
|
|
for (auto& thread : mThreads)
|
|
thread.join();
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents,
|
|
const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& playerTile,
|
|
const std::map<TilePosition, ChangeType>& changedTiles)
|
|
{
|
|
bool playerTileChanged = false;
|
|
{
|
|
auto locked = mPlayerTile.lock();
|
|
playerTileChanged = *locked != playerTile;
|
|
*locked = playerTile;
|
|
}
|
|
|
|
if (!playerTileChanged && changedTiles.empty())
|
|
return;
|
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (playerTileChanged)
|
|
for (JobIt job : mWaiting)
|
|
job->mDistanceToPlayer = getManhattanDistance(job->mChangedTile, playerTile);
|
|
|
|
for (const auto& [changedTile, changeType] : changedTiles)
|
|
{
|
|
if (mPushed.emplace(agentHalfExtents, changedTile).second)
|
|
{
|
|
const auto processTime = changeType == ChangeType::update
|
|
? mLastUpdates[std::tie(agentHalfExtents, changedTile)] + mSettings.get().mMinUpdateInterval
|
|
: std::chrono::steady_clock::time_point();
|
|
|
|
const JobIt it = mJobs.emplace(mJobs.end(), agentHalfExtents, navMeshCacheItem, changedTile,
|
|
changeType, getManhattanDistance(changedTile, playerTile), processTime);
|
|
|
|
if (playerTileChanged)
|
|
mWaiting.push_back(it);
|
|
else
|
|
insertPrioritizedJob(it, mWaiting);
|
|
}
|
|
}
|
|
|
|
if (playerTileChanged)
|
|
std::sort(mWaiting.begin(), mWaiting.end(), LessByJobPriority {});
|
|
|
|
Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs";
|
|
|
|
if (!mWaiting.empty())
|
|
mHasJob.notify_all();
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType)
|
|
{
|
|
if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0)
|
|
return;
|
|
listener.setLabel("Building navigation mesh");
|
|
const std::size_t initialJobsLeft = getTotalJobs();
|
|
std::size_t maxProgress = initialJobsLeft + mThreads.size();
|
|
listener.setProgressRange(maxProgress);
|
|
switch (waitConditionType)
|
|
{
|
|
case WaitConditionType::requiredTilesPresent:
|
|
{
|
|
const int minDistanceToPlayer = waitUntilJobsDoneForNotPresentTiles(initialJobsLeft, maxProgress, listener);
|
|
if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer)
|
|
{
|
|
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
|
|
listener.setProgress(maxProgress);
|
|
}
|
|
break;
|
|
}
|
|
case WaitConditionType::allJobsDone:
|
|
waitUntilAllJobsDone();
|
|
listener.setProgress(maxProgress);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener)
|
|
{
|
|
std::size_t prevJobsLeft = initialJobsLeft;
|
|
std::size_t jobsDone = 0;
|
|
std::size_t jobsLeft = 0;
|
|
const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer;
|
|
const TilePosition playerPosition = *mPlayerTile.lockConst();
|
|
int minDistanceToPlayer = 0;
|
|
const auto isDone = [&]
|
|
{
|
|
jobsLeft = mJobs.size();
|
|
if (jobsLeft == 0)
|
|
{
|
|
minDistanceToPlayer = 0;
|
|
return true;
|
|
}
|
|
minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles);
|
|
return minDistanceToPlayer >= maxDistanceToPlayer;
|
|
};
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone))
|
|
{
|
|
if (maxProgress < jobsLeft)
|
|
{
|
|
maxProgress = jobsLeft + mThreads.size();
|
|
listener.setProgressRange(maxProgress);
|
|
listener.setProgress(jobsDone);
|
|
}
|
|
else if (jobsLeft < prevJobsLeft)
|
|
{
|
|
const std::size_t newJobsDone = prevJobsLeft - jobsLeft;
|
|
jobsDone += newJobsDone;
|
|
prevJobsLeft = jobsLeft;
|
|
listener.increaseProgress(newJobsDone);
|
|
}
|
|
}
|
|
return minDistanceToPlayer;
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::waitUntilAllJobsDone()
|
|
{
|
|
{
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
mDone.wait(lock, [this] { return mJobs.size() == 0; });
|
|
}
|
|
mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); });
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
|
{
|
|
std::size_t jobs = 0;
|
|
|
|
{
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
jobs = mJobs.size();
|
|
}
|
|
|
|
stats.setAttribute(frameNumber, "NavMesh UpdateJobs", jobs);
|
|
|
|
mNavMeshTilesCache.reportStats(frameNumber, stats);
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::process() noexcept
|
|
{
|
|
Log(Debug::Debug) << "Start process navigator jobs by thread=" << std::this_thread::get_id();
|
|
Misc::setCurrentThreadIdlePriority();
|
|
while (!mShouldStop)
|
|
{
|
|
try
|
|
{
|
|
if (JobIt job = getNextJob(); job != mJobs.end())
|
|
{
|
|
const auto processed = processJob(*job);
|
|
unlockTile(job->mAgentHalfExtents, job->mChangedTile);
|
|
if (processed)
|
|
removeJob(job);
|
|
else
|
|
repost(job);
|
|
}
|
|
else
|
|
cleanupLastUpdates();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what();
|
|
}
|
|
}
|
|
Log(Debug::Debug) << "Stop navigator jobs processing by thread=" << std::this_thread::get_id();
|
|
}
|
|
|
|
bool AsyncNavMeshUpdater::processJob(const Job& job)
|
|
{
|
|
Log(Debug::Debug) << "Process job for agent=(" << std::fixed << std::setprecision(2) << job.mAgentHalfExtents << ")"
|
|
" by thread=" << std::this_thread::get_id();
|
|
|
|
const auto start = std::chrono::steady_clock::now();
|
|
|
|
const auto navMeshCacheItem = job.mNavMeshCacheItem.lock();
|
|
|
|
if (!navMeshCacheItem)
|
|
return true;
|
|
|
|
const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);
|
|
const auto playerTile = *mPlayerTile.lockConst();
|
|
const auto offMeshConnections = mOffMeshConnectionsManager.get().get(job.mChangedTile);
|
|
|
|
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
|
|
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache, getUpdateType(job.mChangeType));
|
|
|
|
if (recastMesh != nullptr)
|
|
{
|
|
Version navMeshVersion;
|
|
{
|
|
const auto locked = navMeshCacheItem->lockConst();
|
|
navMeshVersion.mGeneration = locked->getGeneration();
|
|
navMeshVersion.mRevision = locked->getNavMeshRevision();
|
|
}
|
|
mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile,
|
|
Version {recastMesh->getGeneration(), recastMesh->getRevision()},
|
|
navMeshVersion);
|
|
}
|
|
|
|
if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost)
|
|
{
|
|
const std::scoped_lock lock(mMutex);
|
|
mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile));
|
|
}
|
|
else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored)
|
|
{
|
|
const std::scoped_lock lock(mMutex);
|
|
mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile));
|
|
}
|
|
|
|
const auto finish = std::chrono::steady_clock::now();
|
|
|
|
writeDebugFiles(job, recastMesh.get());
|
|
|
|
using FloatMs = std::chrono::duration<float, std::milli>;
|
|
|
|
const auto locked = navMeshCacheItem->lockConst();
|
|
Log(Debug::Debug) << std::fixed << std::setprecision(2) <<
|
|
"Cache updated for agent=(" << job.mAgentHalfExtents << ")" <<
|
|
" tile=" << job.mChangedTile <<
|
|
" status=" << status <<
|
|
" generation=" << locked->getGeneration() <<
|
|
" revision=" << locked->getNavMeshRevision() <<
|
|
" time=" << std::chrono::duration_cast<FloatMs>(finish - start).count() << "ms" <<
|
|
" thread=" << std::this_thread::get_id();
|
|
|
|
return isSuccess(status);
|
|
}
|
|
|
|
JobIt AsyncNavMeshUpdater::getNextJob()
|
|
{
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
const auto threadId = std::this_thread::get_id();
|
|
auto& threadQueue = mThreadsQueues[threadId];
|
|
|
|
while (true)
|
|
{
|
|
bool shouldStop = false;
|
|
|
|
const auto hasJob = [&] {
|
|
shouldStop = mShouldStop;
|
|
return shouldStop
|
|
|| (!mWaiting.empty() && mWaiting.front()->mProcessTime <= std::chrono::steady_clock::now())
|
|
|| !threadQueue.empty();
|
|
};
|
|
|
|
if (!mHasJob.wait_for(lock, std::chrono::milliseconds(10), hasJob))
|
|
{
|
|
if (mJobs.empty())
|
|
mDone.notify_all();
|
|
return mJobs.end();
|
|
}
|
|
|
|
if (shouldStop)
|
|
return mJobs.end();
|
|
|
|
Log(Debug::Debug) << "Got " << mJobs.size() << " navigator jobs and "
|
|
<< threadQueue.size() << " thread jobs by thread=" << std::this_thread::get_id();
|
|
|
|
const JobIt job = threadQueue.empty()
|
|
? getJob(mWaiting, true)
|
|
: getJob(threadQueue, false);
|
|
|
|
if (job == mJobs.end())
|
|
continue;
|
|
|
|
const auto owner = lockTile(job->mAgentHalfExtents, job->mChangedTile);
|
|
|
|
if (owner == threadId)
|
|
{
|
|
mPushed.erase(getAgentAndTile(*job));
|
|
return job;
|
|
}
|
|
|
|
postThreadJob(job, mThreadsQueues[owner]);
|
|
}
|
|
}
|
|
|
|
JobIt AsyncNavMeshUpdater::getJob(std::deque<JobIt>& jobs, bool changeLastUpdate)
|
|
{
|
|
const auto now = std::chrono::steady_clock::now();
|
|
JobIt job = jobs.front();
|
|
|
|
if (job->mProcessTime > now)
|
|
return mJobs.end();
|
|
|
|
jobs.pop_front();
|
|
|
|
if (changeLastUpdate && job->mChangeType == ChangeType::update)
|
|
mLastUpdates[getAgentAndTile(*job)] = now;
|
|
|
|
return job;
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::writeDebugFiles(const Job& job, const RecastMesh* recastMesh) const
|
|
{
|
|
std::string revision;
|
|
std::string recastMeshRevision;
|
|
std::string navMeshRevision;
|
|
if ((mSettings.get().mEnableWriteNavMeshToFile || mSettings.get().mEnableWriteRecastMeshToFile)
|
|
&& (mSettings.get().mEnableRecastMeshFileNameRevision || mSettings.get().mEnableNavMeshFileNameRevision))
|
|
{
|
|
revision = "." + std::to_string((std::chrono::steady_clock::now()
|
|
- std::chrono::steady_clock::time_point()).count());
|
|
if (mSettings.get().mEnableRecastMeshFileNameRevision)
|
|
recastMeshRevision = revision;
|
|
if (mSettings.get().mEnableNavMeshFileNameRevision)
|
|
navMeshRevision = revision;
|
|
}
|
|
if (recastMesh && mSettings.get().mEnableWriteRecastMeshToFile)
|
|
writeToFile(*recastMesh, mSettings.get().mRecastMeshPathPrefix + std::to_string(job.mChangedTile.x())
|
|
+ "_" + std::to_string(job.mChangedTile.y()) + "_", recastMeshRevision);
|
|
if (mSettings.get().mEnableWriteNavMeshToFile)
|
|
if (const auto shared = job.mNavMeshCacheItem.lock())
|
|
writeToFile(shared->lockConst()->getImpl(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::repost(JobIt job)
|
|
{
|
|
if (mShouldStop || job->mTryNumber > 2)
|
|
return;
|
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (mPushed.emplace(job->mAgentHalfExtents, job->mChangedTile).second)
|
|
{
|
|
++job->mTryNumber;
|
|
mWaiting.push_back(job);
|
|
mHasJob.notify_all();
|
|
return;
|
|
}
|
|
|
|
mJobs.erase(job);
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::postThreadJob(JobIt job, std::deque<JobIt>& queue)
|
|
{
|
|
queue.push_back(job);
|
|
mHasJob.notify_all();
|
|
}
|
|
|
|
std::thread::id AsyncNavMeshUpdater::lockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)
|
|
{
|
|
if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)
|
|
return std::this_thread::get_id();
|
|
|
|
auto locked = mProcessingTiles.lock();
|
|
const auto tile = locked->find(std::make_tuple(agentHalfExtents, changedTile));
|
|
if (tile == locked->end())
|
|
{
|
|
const auto threadId = std::this_thread::get_id();
|
|
locked->emplace(std::tie(agentHalfExtents, changedTile), threadId);
|
|
return threadId;
|
|
}
|
|
return tile->second;
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile)
|
|
{
|
|
if (mSettings.get().mAsyncNavMeshUpdaterThreads <= 1)
|
|
return;
|
|
auto locked = mProcessingTiles.lock();
|
|
locked->erase(std::tie(agentHalfExtents, changedTile));
|
|
if (locked->empty())
|
|
mProcessed.notify_all();
|
|
}
|
|
|
|
std::size_t AsyncNavMeshUpdater::getTotalJobs() const
|
|
{
|
|
const std::scoped_lock lock(mMutex);
|
|
return mJobs.size();
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::cleanupLastUpdates()
|
|
{
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
|
const std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
for (auto it = mLastUpdates.begin(); it != mLastUpdates.end();)
|
|
{
|
|
if (now - it->second > mSettings.get().mMinUpdateInterval)
|
|
it = mLastUpdates.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void AsyncNavMeshUpdater::removeJob(JobIt job)
|
|
{
|
|
const std::lock_guard lock(mMutex);
|
|
mJobs.erase(job);
|
|
}
|
|
}
|