diff --git a/src/client/game.cpp b/src/client/game.cpp index f40ac2de2..55ec480b5 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -66,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "shader.h" #include "sky.h" +#include "threading/lambda.h" #include "translation.h" #include "util/basic_macros.h" #include "util/directiontables.h" @@ -1014,12 +1015,6 @@ Game::Game() : Game::~Game() { - delete client; - delete soundmaker; - sound_manager.reset(); - - delete server; // deleted first to stop all server threads - delete hud; delete camera; delete quicktune; @@ -1268,6 +1263,28 @@ void Game::shutdown() sleep_ms(100); } } + + delete client; + delete soundmaker; + sound_manager.reset(); + + auto stop_thread = runInThread([=] { + delete server; + }, "ServerStop"); + + FpsControl fps_control; + fps_control.reset(); + + while (stop_thread->isRunning()) { + m_rendering_engine->run(); + f32 dtime; + fps_control.limit(device, &dtime); + showOverlayMessage(N_("Shutting down..."), dtime, 0, false); + } + + stop_thread->rethrow(); + + // to be continued in Game::~Game } @@ -1373,11 +1390,33 @@ bool Game::createSingleplayerServer(const std::string &map_dir, server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false, nullptr, error_message); - server->start(); - copyServerClientCache(); + auto start_thread = runInThread([=] { + server->start(); + copyServerClientCache(); + }, "ServerStart"); - return true; + input->clear(); + bool success = true; + + FpsControl fps_control; + fps_control.reset(); + + while (start_thread->isRunning()) { + if (!m_rendering_engine->run() || input->cancelPressed()) + success = false; + f32 dtime; + fps_control.limit(device, &dtime); + + if (success) + showOverlayMessage(N_("Creating server..."), dtime, 5); + else + showOverlayMessage(N_("Shutting down..."), dtime, 0, false); + } + + start_thread->rethrow(); + + return success; } void Game::copyServerClientCache() diff --git a/src/debug.h b/src/debug.h index aeea81d47..80497f2b3 100644 --- a/src/debug.h +++ b/src/debug.h @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include #include #include #include "gettime.h" diff --git a/src/threading/lambda.h b/src/threading/lambda.h new file mode 100644 index 000000000..146693457 --- /dev/null +++ b/src/threading/lambda.h @@ -0,0 +1,64 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include +#include "debug.h" +#include "threading/thread.h" + +/** + * Class returned by `runInThread`. + * + * Provides the usual thread methods along with `rethrow()`. +*/ +class LambdaThread : public Thread +{ + friend std::unique_ptr runInThread( + const std::function &, const std::string &); +public: + /// Re-throw a caught exception, if any. Can only be called after thread exit. + void rethrow() + { + sanity_check(!isRunning()); + if (m_exptr) + std::rethrow_exception(m_exptr); + } + +private: + // hide methods + LambdaThread(const std::string &name="") : Thread(name) {} + using Thread::start; + + std::function m_fn; + std::exception_ptr m_exptr; + + void *run() + { + try { + m_fn(); + } catch(...) { + m_exptr = std::current_exception(); + } + return nullptr; + }; +}; + +/** + * Run a lambda in a separate thread. + * + * Exceptions will be caught. + * @param fn function to run + * @param thread_name name for thread + * @return thread object of type `LambdaThread` +*/ +std::unique_ptr runInThread(const std::function &fn, + const std::string &thread_name = "") +{ + std::unique_ptr t(new LambdaThread(thread_name)); + t->m_fn = fn; + t->start(); + return t; +} diff --git a/src/threading/thread.h b/src/threading/thread.h index 2d0641b43..f915be2b3 100644 --- a/src/threading/thread.h +++ b/src/threading/thread.h @@ -59,6 +59,7 @@ public: Thread(const std::string &name=""); virtual ~Thread(); DISABLE_CLASS_COPY(Thread) + // Note: class cannot be moved since other references exist /* * Begins execution of a new thread at the pure virtual method Thread::run().