From 29abff192c29d9a4dc1d06421a0aac19ff549ec8 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sun, 13 Feb 2022 21:12:52 +0100 Subject: [PATCH] Use thread pool for map generation --- deps/dragonstd | 2 +- snapshot.sh | 2 +- src/client/client_map.c | 2 +- src/server/server_config.c | 8 +- src/server/server_config.h | 1 + src/server/server_map.c | 164 ++++++++++++++++++++----------------- src/server/server_map.h | 16 ++-- 7 files changed, 111 insertions(+), 84 deletions(-) diff --git a/deps/dragonstd b/deps/dragonstd index 04c9384..0f30ec8 160000 --- a/deps/dragonstd +++ b/deps/dragonstd @@ -1 +1 @@ -Subproject commit 04c93844e4e4185d91950f0f5473bc88a84b5461 +Subproject commit 0f30ec889c7a70ab7f7b79836d1a34ddf8659a47 diff --git a/snapshot.sh b/snapshot.sh index f03ceed..6d4363d 100755 --- a/snapshot.sh +++ b/snapshot.sh @@ -11,7 +11,7 @@ if ! (cmake -B . -S ../src -DCMAKE_BUILD_TYPE=Release -DRESSOURCE_PATH="\"\"" && fi cp Dragonblocks DragonblocksServer .. cd .. -rm -rf .git* deps src build BUILDING.md snapshot.sh upload.sh DragonblocksAlpha-*.zip screenshot-*.png +rm -rf .git* deps src build BUILDING.md snapshot.sh upload.sh DragonblocksAlpha-*.zip DragonblocksAlpha screenshot-*.png cd .. mv .build DragonblocksAlpha VERSION=`git tag --points-at HEAD` diff --git a/src/client/client_map.c b/src/client/client_map.c index 5d2d50e..f617341 100644 --- a/src/client/client_map.c +++ b/src/client/client_map.c @@ -168,7 +168,7 @@ void client_map_init() client_map.meshgen_threads = malloc(sizeof *client_map.meshgen_threads * client_config.meshgen_threads); for (unsigned int i = 0; i < client_config.meshgen_threads; i++) - client_map.meshgen_threads[i] = 0; + client_map.meshgen_threads[i] = 0; // why } // ClientMap singleton destructor diff --git a/src/server/server_config.c b/src/server/server_config.c index ec79abc..e4d146e 100644 --- a/src/server/server_config.c +++ b/src/server/server_config.c @@ -3,6 +3,7 @@ struct ServerConfig server_config = { .simulation_distance = 10, + .mapgen_threads = 4, }; __attribute__((constructor)) static void server_config_init() @@ -13,6 +14,11 @@ __attribute__((constructor)) static void server_config_init() .key = "simulation_distance", .value = &server_config.simulation_distance, }, - }, 1); + { + .type = CT_UINT, + .key = "mapgen_threads", + .value = &server_config.mapgen_threads, + }, + }, 2); } diff --git a/src/server/server_config.h b/src/server/server_config.h index 992a3c8..4effe33 100644 --- a/src/server/server_config.h +++ b/src/server/server_config.h @@ -3,6 +3,7 @@ extern struct ServerConfig { unsigned int simulation_distance; + unsigned int mapgen_threads; } server_config; #endif diff --git a/src/server/server_map.c b/src/server/server_map.c index 3cadbc1..854afe1 100644 --- a/src/server/server_map.c +++ b/src/server/server_map.c @@ -10,6 +10,7 @@ #include "server/server_map.h" #include "util.h" +// this file is too long struct ServerMap server_map; // utility functions @@ -68,15 +69,15 @@ static void list_send_block(void *key, unused void *value, unused void *arg) pthread_mutex_unlock(&block->mtx); } -// pthread start routine for mapgen thread -static void *mapgen_thread(void *arg) +// me when the +static void mapgen_step() { - MapBlock *block = arg; - MapBlockExtraData *extra = block->extra; + MapBlock *block = queue_dequeue(server_map.mapgen_tasks); - pthread_mutex_lock(&block->mtx); - extra->state = MBS_GENERATING; - pthread_mutex_unlock(&block->mtx); + if (! block) + return; + + MapBlockExtraData *extra = block->extra; List changed_blocks = list_create(NULL); list_put(&changed_blocks, block, NULL); @@ -89,32 +90,33 @@ static void *mapgen_thread(void *arg) list_clear_func(&changed_blocks, &list_send_block, NULL); - pthread_mutex_lock(&server_map.joining_threads_mtx); - if (! server_map.joining_threads) { - pthread_mutex_lock(&server_map.mapgen_threads_mtx); - list_delete(&server_map.mapgen_threads, &extra->mapgen_thread); - pthread_mutex_unlock(&server_map.mapgen_threads_mtx); - } - pthread_mutex_unlock(&server_map.joining_threads_mtx); - - return NULL; + pthread_mutex_lock(&server_map.num_blocks_mtx); + server_map.num_blocks--; + pthread_mutex_unlock(&server_map.num_blocks_mtx); } -// launch mapgen thread for block -// block mutex has to be locked -static void launch_mapgen_thread(MapBlock *block) +// there was a time when i wrote actually useful comments lol +static void *mapgen_thread(unused void *arg) { - pthread_mutex_lock(&server_map.mapgen_threads_mtx); - pthread_t *thread_ptr = &((MapBlockExtraData *) block->extra)->mapgen_thread; - pthread_create(thread_ptr, NULL, mapgen_thread, block); - list_put(&server_map.mapgen_threads, thread_ptr, NULL); - pthread_mutex_unlock(&server_map.mapgen_threads_mtx); + while (! server_map.cancel) + mapgen_step(); + + return NULL; } -// list_clear_func callback used to join running generator threads on shutdown -static void list_join_thread(void *key, unused void *value, unused void *arg) +// enqueue block +static void generate_block(MapBlock *block) { - pthread_join(*(pthread_t *) key, NULL); + if (server_map.cancel) + return; + + pthread_mutex_lock(&server_map.num_blocks_mtx); + server_map.num_blocks++; + pthread_mutex_unlock(&server_map.num_blocks_mtx); + + MapBlockExtraData *extra = block->extra; + extra->state = MBS_GENERATING; + queue_enqueue(server_map.mapgen_tasks, block); } // map callbacks @@ -194,22 +196,6 @@ static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg) send_block_to_near(block); } -// join all map generation threads -static void join_mapgen_threads() -{ - pthread_mutex_lock(&server_map.joining_threads_mtx); - server_map.joining_threads = true; - pthread_mutex_unlock(&server_map.joining_threads_mtx); - - pthread_mutex_lock(&server_map.mapgen_threads_mtx); - list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL); - pthread_mutex_unlock(&server_map.mapgen_threads_mtx); - - pthread_mutex_lock(&server_map.joining_threads_mtx); - server_map.joining_threads = false; - pthread_mutex_unlock(&server_map.joining_threads_mtx); -} - // generate a hut for new players to spawn in static void generate_spawn_hut() { @@ -290,18 +276,30 @@ void server_map_init() .set_node = &on_set_node, .after_set_node = &on_after_set_node, }); - server_map.joining_threads = false; - server_map.mapgen_threads = list_create(NULL); - pthread_mutex_init(&server_map.joining_threads_mtx, NULL); - pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL); + + server_map.cancel = false; + server_map.mapgen_tasks = queue_create(); + server_map.mapgen_threads = malloc(sizeof *server_map.mapgen_threads * server_config.mapgen_threads); + server_map.num_blocks = 0; + pthread_mutex_init(&server_map.num_blocks_mtx, NULL); + + for (unsigned int i = 0; i < server_config.mapgen_threads; i++) + pthread_create(&server_map.mapgen_threads[i], NULL, &mapgen_thread, NULL); } // ServerMap singleton destructor void server_map_deinit() { - join_mapgen_threads(); - pthread_mutex_destroy(&server_map.joining_threads_mtx); - pthread_mutex_destroy(&server_map.mapgen_threads_mtx); + queue_finish(server_map.mapgen_tasks); + server_map.cancel = true; + queue_cancel(server_map.mapgen_tasks); + + for (unsigned int i = 0; i < server_config.mapgen_threads; i++) + pthread_join(server_map.mapgen_threads[i], NULL); + free(server_map.mapgen_threads); + + pthread_mutex_destroy(&server_map.num_blocks_mtx); + queue_delete(server_map.mapgen_tasks); map_delete(server_map.map); } @@ -312,11 +310,11 @@ void server_map_requested_block(ServerPlayer *player, v3s32 pos) MapBlock *block = map_get_block(server_map.map, pos, true); pthread_mutex_lock(&block->mtx); - MapBlockExtraData *extra = block->extra; + MapBlockExtraData *extra = block->extra; switch (extra->state) { case MBS_CREATED: - launch_mapgen_thread(block); + generate_block(block); break; case MBS_GENERATING: @@ -325,47 +323,65 @@ void server_map_requested_block(ServerPlayer *player, v3s32 pos) case MBS_READY: send_block(player, block); }; + pthread_mutex_unlock(&block->mtx); } } +static void update_percentage() +{ + static s32 total = 3 * 3 * 21; + static s32 done = -1; + static s32 last_percentage = -1; + + if (done < total) + done++; + + pthread_mutex_lock(&server_map.num_blocks_mtx); + s32 percentage = 100.0 * (done - server_map.num_blocks) / total; + pthread_mutex_unlock(&server_map.num_blocks_mtx); + + if (percentage > last_percentage) { + last_percentage = percentage; + printf("Preparing spawn... %d%%\n", percentage); + } + +} + // prepare spawn region void server_map_prepare_spawn() { - s32 done = 0; - s32 dist = server_config.simulation_distance; - s32 total = (dist * 2 + 1); - total *= total * total; - s32 last_percentage = -1; - - for (s32 x = -dist; x <= (s32) dist; x++) { - for (s32 y = -dist; y <= (s32) dist; y++) { - for (s32 z = -dist; z <= (s32) dist; z++) { - if (interrupt->done) { - join_mapgen_threads(); + update_percentage(); + + for (s32 x = -1; x <= (s32) 1; x++) { + for (s32 y = -10; y <= (s32) 10; y++) { + for (s32 z = -1; z <= (s32) 1; z++) { + if (interrupt->done) return; - } MapBlock *block = map_get_block(server_map.map, (v3s32) {x, y, z}, true); pthread_mutex_lock(&block->mtx); if (((MapBlockExtraData *) block->extra)->state == MBS_CREATED) - launch_mapgen_thread(block); + generate_block(block); pthread_mutex_unlock(&block->mtx); - done++; - - s32 percentage = 100.0 * done / total; - - if (percentage > last_percentage) { - last_percentage = percentage; - printf("Preparing spawn... %d%%\n", percentage); - } + update_percentage(); } } } - join_mapgen_threads(); + while (true) { + pthread_mutex_lock(&server_map.num_blocks_mtx); + bool done = (server_map.num_blocks == 0); + pthread_mutex_unlock(&server_map.num_blocks_mtx); + + if (done) + break; + + update_percentage(); + sched_yield(); + } s64 saved_spawn_height; if (database_load_meta("spawn_height", &saved_spawn_height)) { diff --git a/src/server/server_map.h b/src/server/server_map.h index da7c2d4..68f3a3e 100644 --- a/src/server/server_map.h +++ b/src/server/server_map.h @@ -1,8 +1,11 @@ #ifndef _SERVER_MAP_H_ #define _SERVER_MAP_H_ +#include +#include #include #include +#include #include "map.h" #include "server/server_player.h" #include "types.h" @@ -37,12 +40,13 @@ typedef struct } MapBlockExtraData; extern struct ServerMap { - Map *map; // map object, data is stored here - bool joining_threads; // prevent threads from removing themselves from the thread list if thread list is being cleared anyway - pthread_mutex_t joining_threads_mtx; // mutex to protect joining threads - List mapgen_threads; // a list of mapgen threads (need to be joined before shutdown) - pthread_mutex_t mapgen_threads_mtx; // mutex to protect mapgen thread list - s32 spawn_height; // height to spawn players at + atomic_bool cancel; // remove the smooth + Map *map; // map object, data is stored here + Queue *mapgen_tasks; // this is terry the fat shark + pthread_t *mapgen_threads; // thread pool + s32 spawn_height; // elevation to spawn players at + unsigned int num_blocks; // number of enqueued / generating blocks + pthread_mutex_t num_blocks_mtx; // lock to protect the above } server_map; // ServerMap singleton void server_map_init(); // ServerMap singleton constructor -- 2.44.0