refactor(server_world): replace future-based chunk generation with queue

Remove `std::future` per-chunk tracking and poll_finished_chunks().
Instead, generation tasks push completed chunks to a concurrent queue
consumed during update. This eliminates wait_all_chunk_tasks() and
simplifies synchronization.
This commit is contained in:
2026-06-29 11:58:10 +08:00
parent 50a6a8c0ef
commit 8faf4c9a81
2 changed files with 38 additions and 80 deletions

View File

@@ -11,7 +11,6 @@
#include "world/block_change.pb.h" #include "world/block_change.pb.h"
#include <absl/container/flat_hash_set.h> #include <absl/container/flat_hash_set.h>
#include <future>
#include <shared_mutex> #include <shared_mutex>
#include <tbb/concurrent_hash_map.h> #include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_queue.h> #include <tbb/concurrent_queue.h>
@@ -101,10 +100,6 @@ private:
ChunkPos pos; ChunkPos pos;
}; };
struct PendingChunk { struct PendingChunk {
std::unique_ptr<ServerChunk> chunk;
std::future<void> future;
};
struct FinishedChunk {
ChunkPos pos; ChunkPos pos;
std::unique_ptr<ServerChunk> chunk; std::unique_ptr<ServerChunk> chunk;
}; };
@@ -112,8 +107,7 @@ private:
using ChunkHashMap = using ChunkHashMap =
tbb::concurrent_hash_map<ChunkPos, ChunkEntity, ChunkPos::TBBHash>; tbb::concurrent_hash_map<ChunkPos, ChunkEntity, ChunkPos::TBBHash>;
using PlayerHashMap = std::unordered_map<std::string, ServerPlayer>; using PlayerHashMap = std::unordered_map<std::string, ServerPlayer>;
using PendingChunkHashMap = using NewChunkVector = std::vector<PendingChunk>;
std::unordered_map<ChunkPos, PendingChunk, ChunkPos::Hash>;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>; using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>; using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
@@ -125,8 +119,7 @@ private:
// key = uuid // key = uuid
PlayerHashMap m_players; PlayerHashMap m_players;
ChunkHashMap m_chunks; ChunkHashMap m_chunks;
PendingChunkHashMap m_new_chunks; NewChunkVector m_new_chunks;
std::vector<FinishedChunk> m_new_finished_chunk;
CaveCarver m_cave_carcer; CaveCarver m_cave_carcer;
RiverWorm m_river_worm; RiverWorm m_river_worm;
@@ -167,6 +160,7 @@ private:
tbb::concurrent_unordered_map<std::string, Timer> m_timers; tbb::concurrent_unordered_map<std::string, Timer> m_timers;
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests; tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue;
void init_chunks(); void init_chunks();
@@ -177,8 +171,7 @@ private:
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&, void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
const ChunkPosSet&); const ChunkPosSet&);
void submit_new_chunks(const std::string& uuid); void submit_new_chunks(const std::string& uuid);
void poll_finished_chunks(); // void wait_all_chunk_tasks();
void wait_all_chunk_tasks();
void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now); void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now);

View File

@@ -28,28 +28,13 @@ void ServerWorld::stop() {
send_server_stop(); send_server_stop();
stop_gen_thread(); stop_gen_thread();
stop_server_thread(); stop_server_thread();
wait_all_chunk_tasks(); // wait_all_chunk_tasks();
stop_thread_pool(); stop_thread_pool();
m_finished_queue.clear();
m_chunks.clear(); m_chunks.clear();
} }
void ServerWorld::wait_all_chunk_tasks() {
std::lock_guard lock(m_new_chunk_mutex);
for (auto& [pos, task] : m_new_chunks) {
if (task.future.valid()) {
try {
task.future.get();
} catch (const std::exception& e) {
Logger::error("Chunk generation failed: {}", e.what());
continue;
}
} else {
Logger::error("Chunk {} {} not started gen task", pos.x, pos.z);
}
}
}
void ServerWorld::update_ref_count(const ChunkPosSet& old, void ServerWorld::update_ref_count(const ChunkPosSet& old,
const ChunkPosSet& now) { const ChunkPosSet& now) {
@@ -265,7 +250,7 @@ void ServerWorld::gen_chunks_internal(const std::string& uuid) {
// Create new chunk // Create new chunk
std::lock_guard lock(m_new_chunk_mutex); std::lock_guard lock(m_new_chunk_mutex);
for (auto& pos : need_gen_chunks_pos) { for (auto& pos : need_gen_chunks_pos) {
m_new_chunks.emplace( m_new_chunks.emplace_back(
pos, std::make_unique<ServerChunk>(ServerChunk(*this, pos))); pos, std::make_unique<ServerChunk>(ServerChunk(*this, pos)));
} }
} }
@@ -321,25 +306,24 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
switch (m_chunk_load_style) { switch (m_chunk_load_style) {
case RANDOM: case RANDOM:
// Enqueue directly in random order // Enqueue directly in random order
for (auto& [pos, task] : m_new_chunks) { for (auto& task : m_new_chunks) {
if (!task.future.valid()) {
task.future = pool_ptr->enqueue([&task, this]() {
pool_ptr->enqueue([&task]() { task.chunk->gen_chunk(); }); std::unique_ptr<ServerChunk> chunk{std::move(task.chunk)};
} chunk->gen_chunk();
m_finished_queue.push(std::move(chunk));
});
} }
break; break;
case CENTER: { case CENTER: {
std::vector<std::pair<ChunkPos, PendingChunk*>> tasks; std::vector<std::pair<ChunkPos, PendingChunk*>> tasks;
for (auto& [pos, task] : m_new_chunks) { for (auto& task : m_new_chunks) {
if (!task.future.valid()) {
tasks.emplace_back(pos, &task); tasks.emplace_back(task.pos, &task);
}
} }
glm::vec3 player_pos = get_player_pos(uuid); glm::vec3 player_pos = get_player_pos(uuid);
ChunkPos player_chunk_pos = get_chunk_pos(player_pos.x, player_pos.z);
auto dist2 = [player_pos](ChunkPos chunk_pos) { auto dist2 = [player_chunk_pos](ChunkPos chunk_pos) {
ChunkPos player_chunk_pos =
get_chunk_pos(player_pos.x, player_pos.z);
float dx = player_chunk_pos.x - chunk_pos.x; float dx = player_chunk_pos.x - chunk_pos.x;
float dz = player_chunk_pos.z - chunk_pos.z; float dz = player_chunk_pos.z - chunk_pos.z;
return dx * dx + dz * dz; return dx * dx + dz * dz;
@@ -350,39 +334,16 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
return dist2(a.first) < dist2(b.first); return dist2(a.first) < dist2(b.first);
}); });
for (auto& [pos, task] : tasks) { for (auto& [pos, task] : tasks) {
if (!task->future.valid()) {
task->future =
pool_ptr->enqueue([task]() { task->chunk->gen_chunk(); });
}
}
}
}
}
void ServerWorld::poll_finished_chunks() { pool_ptr->enqueue([this, chunk = std::move(task->chunk)]() mutable {
m_new_finished_chunk.clear(); chunk->gen_chunk();
std::lock_guard lock(m_new_chunk_mutex); m_finished_queue.push(std::move(chunk));
std::erase_if(
m_new_chunks, [&](std::pair<const ChunkPos, PendingChunk>& pair) {
auto& pending = pair.second;
if (!pending.future.valid()) {
return false;
}
if (pending.future.wait_for(0ms) != std::future_status::ready) {
return false;
}
try {
pending.future.get();
} catch (const std::exception& e) {
Logger::error("Chunk generation failed: {}", e.what());
return true;
}
// Spawn complete, move away
m_new_finished_chunk.emplace_back(pair.first,
std::move(pending.chunk));
return true;
}); });
}
} break;
}
m_new_chunks.clear();
} }
void ServerWorld::start_gen_thread() { void ServerWorld::start_gen_thread() {
@@ -554,7 +515,6 @@ void ServerWorld::rebuild_world() {
m_river_worm.reload(ChunkGenerator::seed()); m_river_worm.reload(ChunkGenerator::seed());
m_chunks.clear(); m_chunks.clear();
m_new_finished_chunk.clear();
{ {
std::lock_guard lock(m_new_chunk_mutex); std::lock_guard lock(m_new_chunk_mutex);
@@ -579,19 +539,24 @@ void ServerWorld::rebuild_world() {
} }
void ServerWorld::update() { void ServerWorld::update() {
poll_finished_chunks(); // poll_finished_chunks();
{ {
bool consumed = false; bool consumed = false;
std::unique_ptr<ServerChunk> chunk;
for (auto& x : m_new_finished_chunk) { while (m_finished_queue.try_pop(chunk)) {
if (!chunk) {
Logger::error("Finished Queue has nullptr Chunk");
return;
}
chunk_acc acc; chunk_acc acc;
if (!m_chunks.find(acc, x.pos)) { auto pos = chunk->get_chunk_pos();
if (!m_chunks.find(acc, pos)) {
Logger::error( Logger::error(
"New Chunk {} {} not Find, don't move to m_chunks", x.pos.x, "New Chunk {} {} not Find, don't move to m_chunks", pos.x,
x.pos.z); pos.z);
continue; continue;
} }
acc->second.chunk = std::move(x.chunk); acc->second.chunk = std::move(chunk);
acc->second.state = ChunkState::READY; acc->second.state = ChunkState::READY;
consumed = true; consumed = true;
} }