Fix local server startup and shutdown blocking the main thread

Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
Gregor Parzefall 2024-04-02 14:57:25 +02:00 committed by sfan5
parent b2982a6f14
commit fd8e02195e
4 changed files with 113 additions and 10 deletions

@ -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);
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()

@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include <iostream>
#include <exception>
#include <cassert>
#include "gettime.h"

64
src/threading/lambda.h Normal file

@ -0,0 +1,64 @@
// Minetest
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <exception>
#include <functional>
#include <memory>
#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<LambdaThread> runInThread(
const std::function<void()> &, 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<void()> 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<LambdaThread> runInThread(const std::function<void()> &fn,
const std::string &thread_name = "")
{
std::unique_ptr<LambdaThread> t(new LambdaThread(thread_name));
t->m_fn = fn;
t->start();
return t;
}

@ -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().