diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 7cb1fb9..2ec0596 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -105,6 +105,12 @@ public: } private: + std::atomic m_is_pending_delete_queue_free{false}; + std::mutex m_delete_vbo_mutex; + std::mutex m_delete_vao_mutex; + std::vector> m_pending_delete_vbo; + std::vector> m_pending_delete_vao; + enum class ChunkLoadStyle { RANDOM, CENTER }; using ChunkHashMap = tbb::concurrent_hash_map, @@ -130,15 +136,11 @@ private: std::vector m_planes; std::jthread m_client_thread; - std::mutex m_delete_vbo_mutex; - std::mutex m_delete_vao_mutex; mutable std::shared_mutex m_player_info_mutex; tbb::concurrent_queue> m_pending_upload_queue; tbb::concurrent_queue m_dirty_chunk_queue; tbb::concurrent_queue m_pending_sound; - std::vector> m_pending_delete_vbo; - std::vector> m_pending_delete_vao; std::deque m_dirty_queue; std::vector m_render_snapshots; diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index f364b3c..d44e5de 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -25,11 +25,22 @@ ClientWorld::ClientWorld(AudioEngine& auido, Config& config) : m_player(*this), m_audio(auido), m_config(config) {} ClientWorld::~ClientWorld() { + m_client->close(); + stop_client_thread(); stop_thread_pool(); + // Must first clean up and push the generated chunk data into + // m_pending_delete_vbo and m_pending_delete_vao; cannot delete them in the + // destructor, otherwise it will cause leaks and use-after-free. + m_dirty_chunk_queue.clear(); + m_pending_upload_queue.clear(); m_chunks.clear(); + if (m_is_pending_delete_queue_free.exchange(true)) { + return; + } + { std::lock_guard lk(m_delete_vbo_mutex); m_pending_delete_vbo.clear(); @@ -262,10 +273,18 @@ void ClientWorld::set_block(const glm::ivec3& block_pos, unsigned id) { } } void ClientWorld::push_delete_vbo(std::unique_ptr& vbo) { + if (m_is_pending_delete_queue_free) { + Logger::error("Push delete vbo Use After Free"); + return; + } std::lock_guard lk(m_delete_vbo_mutex); m_pending_delete_vbo.push_back(std::move(vbo)); } void ClientWorld::push_delete_vao(std::unique_ptr& vao) { + if (m_is_pending_delete_queue_free) { + Logger::error("Push delete vao Use After Free"); + return; + } std::lock_guard lk(m_delete_vao_mutex); m_pending_delete_vao.push_back(std::move(vao)); }