mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-11-04 09:15:29 +01:00 
			
		
		
		
	Fix local server startup and shutdown blocking the main thread
Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
		@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
							
							
						
						
									
										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().
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user