瀏覽代碼

Shared memory refactoring and command line control options

Now client accept following options:
 --disable-shm - disable shared memory usage
 --enable-shm-uuid - use UUID for shared memory identifier
UUID is useful when a lot of clients starting simultaneously.
Needed for testing and was easier to implement than alternatives.
Arseniy Shestakov 8 年之前
父節點
當前提交
1a60c1a94b
共有 7 個文件被更改,包括 77 次插入62 次删除
  1. 3 0
      Global.h
  2. 5 0
      client/CMT.cpp
  3. 20 24
      client/Client.cpp
  4. 3 2
      client/Client.h
  5. 32 12
      lib/Interprocess.h
  6. 12 24
      server/CVCMIServer.cpp
  7. 2 0
      server/CVCMIServer.h

+ 3 - 0
Global.h

@@ -185,6 +185,9 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/variant.hpp>
 #include <boost/math/special_functions/round.hpp>
 #include <boost/multi_array.hpp>
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_io.hpp>
+#include <boost/uuid/uuid_generators.hpp>
 
 #ifndef M_PI
 #  define M_PI 3.14159265358979323846

+ 5 - 0
client/CMT.cpp

@@ -260,6 +260,8 @@ int main(int argc, char** argv)
 	opts.add_options()
 		("help,h", "display help and exit")
 		("version,v", "display version information and exit")
+		("disable-shm", "force disable shared memory usage")
+		("enable-shm-uuid", "use UUID for shared memory identifier")
 		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
 		("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
 		("testmap", po::value<std::string>(), "")
@@ -345,6 +347,9 @@ int main(int argc, char** argv)
 		session["headless"].Bool() = true;
 		session["onlyai"].Bool() = true;
 	}
+	// Shared memory options
+	session["disable-shm"].Bool() = vm.count("disable-shm");
+	session["enable-shm-uuid"].Bool() = vm.count("enable-shm-uuid");
 
 	// Init special testing settings
 	session["serverport"].Integer() = vm.count("serverport") ? vm["serverport"].as<si64>() : 0;

+ 20 - 24
client/Client.cpp

@@ -41,9 +41,7 @@
 #include "CMT.h"
 
 extern std::string NAME;
-#ifndef VCMI_ANDROID
-namespace intpr = boost::interprocess;
-#else
+#ifdef VCMI_ANDROID
 #include "lib/CAndroidVMHelper.h"
 #endif
 
@@ -1019,13 +1017,7 @@ void CServerHandler::waitForServer()
 
 #ifndef VCMI_ANDROID
 	if(shared)
-	{
-		intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
-		while(!shared->sr->ready)
-		{
-			shared->sr->cond.wait(slock);
-		}
-	}
+		shared->sr->waitTillReady();
 #else
 	logNetwork->infoStream() << "waiting for server";
 	while (!androidTestServerReadyFlag.load())
@@ -1042,15 +1034,7 @@ void CServerHandler::waitForServer()
 
 CConnection * CServerHandler::connectToServer()
 {
-#ifndef VCMI_ANDROID
-	if(shared)
-	{
-		if(!shared->sr->ready)
-			waitForServer();
-	}
-#else
 	waitForServer();
-#endif
 
 	th.update(); //put breakpoint here to attach to server before it does something stupid
 
@@ -1084,15 +1068,21 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 	serverThread = nullptr;
 	shared = nullptr;
 	verbose = true;
+	uuid = boost::uuids::to_string(boost::uuids::random_generator()());
 
 #ifndef VCMI_ANDROID
-	if(DO_NOT_START_SERVER)
+	if(DO_NOT_START_SERVER || settings["session"]["disable-shm"].Bool())
 		return;
 
-	boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
+	std::string sharedMemoryName = "vcmi_memory";
+	if(settings["session"]["enable-shm-uuid"].Bool())
+	{
+		//used or automated testing when multiple clients start simultaneously
+		sharedMemoryName += "_" + uuid;
+	}
 	try
 	{
-		shared = new SharedMem();
+		shared = new SharedMemory(sharedMemoryName, true);
 	}
 	catch(...)
 	{
@@ -1115,11 +1105,17 @@ void CServerHandler::callServer()
 #ifndef VCMI_ANDROID
 	setThreadName("CServerHandler::callServer");
 	const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
-	const std::string comm = VCMIDirs::get().serverPath().string()
+	std::string comm = VCMIDirs::get().serverPath().string()
 		+ " --port=" + getDefaultPortStr()
 		+ " --run-by-client"
-		+ (shared ? " --use-shm" : "")
-		+ " > \"" + logName + '\"';
+		+ " --uuid=" + uuid;
+	if(shared)
+	{
+		comm += " --enable-shm";
+		if(settings["session"]["enable-shm-uuid"].Bool())
+			comm += " --enable-shm-uuid";
+	}
+	comm += " > \"" + logName + '\"';
 
 	int result = std::system(comm.c_str());
 	if (result == 0)

+ 3 - 2
client/Client.h

@@ -28,7 +28,7 @@ class CGameInterface;
 class CConnection;
 class CCallback;
 struct BattleAction;
-struct SharedMem;
+struct SharedMemory;
 class CClient;
 class CScriptingModule;
 struct CPathsInfo;
@@ -46,7 +46,8 @@ public:
 
 	CStopWatch th;
 	boost::thread *serverThread; //thread that called system to run server
-	SharedMem *shared; //interprocess memory (for waiting for server)
+	SharedMemory * shared;
+	std::string uuid;
 	bool verbose; //whether to print log msgs
 
 	//functions setting up local server

+ 32 - 12
lib/Interprocess.h

@@ -20,8 +20,8 @@ struct ServerReady
 {
 	bool ready;
 	uint16_t port; //ui16?
-	boost::interprocess::interprocess_mutex  mutex;
-	boost::interprocess::interprocess_condition  cond;
+	boost::interprocess::interprocess_mutex mutex;
+	boost::interprocess::interprocess_condition cond;
 
 	ServerReady()
 	{
@@ -29,10 +29,19 @@ struct ServerReady
 		port = 0;
 	}
 
-	void setToTrueAndNotify(uint16_t Port)
+	void waitTillReady()
 	{
+		boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> slock(mutex);
+		while(!ready)
 		{
-			boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex); 
+			cond.wait(slock);
+		}
+	}
+
+	void setToReadyAndNotify(const uint16_t Port)
+	{
+		{
+			boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex);
 			ready = true;
 			port = Port;
 		}
@@ -40,22 +49,33 @@ struct ServerReady
 	}
 };
 
-struct SharedMem 
+struct SharedMemory
 {
+	const char * name;
 	boost::interprocess::shared_memory_object smo;
-	boost::interprocess::mapped_region *mr;
-	ServerReady *sr;
+	boost::interprocess::mapped_region * mr;
+	ServerReady * sr;
 	
-	SharedMem() //c-tor
-		:smo(boost::interprocess::open_or_create,"vcmi_memory",boost::interprocess::read_write) 
+	SharedMemory(std::string Name, bool initialize = false)
+		: name(Name.c_str())
 	{
+		if(initialize)
+		{
+			//if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
+			boost::interprocess::shared_memory_object::remove(name);
+		}
+		smo = boost::interprocess::shared_memory_object(boost::interprocess::open_or_create, name, boost::interprocess::read_write);
 		smo.truncate(sizeof(ServerReady));
 		mr = new boost::interprocess::mapped_region(smo,boost::interprocess::read_write);
-		sr = new(mr->get_address())ServerReady();
+		if(initialize)
+			sr = new(mr->get_address())ServerReady();
+		else
+			sr = reinterpret_cast<ServerReady*>(mr->get_address());
 	};
-	~SharedMem() //d-tor
+
+	~SharedMemory()
 	{
 		delete mr;
-		boost::interprocess::shared_memory_object::remove("vcmi_memory");
+		boost::interprocess::shared_memory_object::remove(name);
 	}
 };

+ 12 - 24
server/CVCMIServer.cpp

@@ -41,9 +41,6 @@
 
 std::string NAME_AFFIX = "server";
 std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
-#ifndef VCMI_ANDROID
-namespace intpr = boost::interprocess;
-#endif
 std::atomic<bool> serverShuttingDown(false);
 
 boost::program_options::variables_map cmdLineOptions;
@@ -326,7 +323,7 @@ void CPregameServer::startListeningThread(CConnection * pc)
 }
 
 CVCMIServer::CVCMIServer()
-	: port(3030), io(new boost::asio::io_service()), firstConnection(nullptr)
+	: port(3030), io(new boost::asio::io_service()), firstConnection(nullptr), shared(nullptr)
 {
 	logNetwork->trace("CVCMIServer created!");
 	if(cmdLineOptions.count("port"))
@@ -339,7 +336,7 @@ CVCMIServer::CVCMIServer()
 	catch(...)
 	{
 		logNetwork->info("Port %d is busy, trying to use random port instead", port);
-		if(cmdLineOptions.count("run-by-client") && !cmdLineOptions.count("use-shm"))
+		if(cmdLineOptions.count("run-by-client") && !cmdLineOptions.count("enable-shm"))
 		{
 			logNetwork->error("Cant pass port number to client without shared memory!", port);
 			exit(0);
@@ -425,24 +422,14 @@ void CVCMIServer::newPregame()
 void CVCMIServer::start()
 {
 #ifndef VCMI_ANDROID
-	ServerReady *sr = nullptr;
-	intpr::mapped_region *mr;
-	if(cmdLineOptions.count("use-shm"))
+	if(cmdLineOptions.count("enable-shm"))
 	{
-		try
-		{
-			intpr::shared_memory_object smo(intpr::open_only,"vcmi_memory",intpr::read_write);
-			smo.truncate(sizeof(ServerReady));
-			mr = new intpr::mapped_region(smo,intpr::read_write);
-			sr = reinterpret_cast<ServerReady*>(mr->get_address());
-		}
-		catch(...)
+		std::string sharedMemoryName = "vcmi_memory";
+		if(cmdLineOptions.count("enable-shm-uuid") && cmdLineOptions.count("uuid"))
 		{
-			intpr::shared_memory_object smo(intpr::create_only,"vcmi_memory",intpr::read_write);
-			smo.truncate(sizeof(ServerReady));
-			mr = new intpr::mapped_region(smo,intpr::read_write);
-			sr = new(mr->get_address())ServerReady();
+			sharedMemoryName += "_" + cmdLineOptions["uuid"].as<std::string>();
 		}
+		shared = new SharedMemory(sharedMemoryName);
 	}
 #endif
 
@@ -460,10 +447,9 @@ void CVCMIServer::start()
 				logNetwork->info("Sending server ready message to client");
 			}
 #else
-			if(cmdLineOptions.count("use-shm"))
+			if(shared)
 			{
-				sr->setToTrueAndNotify(port);
-				delete mr;
+				shared->sr->setToReadyAndNotify(port);
 			}
 #endif
 
@@ -557,7 +543,9 @@ static void handleCommandOptions(int argc, char *argv[])
 		("help,h", "display help and exit")
 		("version,v", "display version information and exit")
 		("run-by-client", "indicate that server launched by client on same machine")
-		("use-shm", "enable usage of shared memory")
+		("uuid", po::value<std::string>(), "")
+		("enable-shm-uuid", "use UUID for shared memory identifier")
+		("enable-shm", "enable usage of shared memory")
 		("port", po::value<ui16>(), "port at which server will listen to connections from client")
 		("resultsFile", po::value<std::string>()->default_value("./results.txt"), "file to which the battle result will be appended. Used only in the DUEL mode.");
 

+ 2 - 0
server/CVCMIServer.h

@@ -17,6 +17,7 @@ class CMapInfo;
 class CConnection;
 struct CPackForSelectionScreen;
 class CGameHandler;
+struct SharedMemory;
 
 namespace boost
 {
@@ -46,6 +47,7 @@ class CVCMIServer
 	ui16 port;
 	boost::asio::io_service *io;
 	TAcceptor * acceptor;
+	SharedMemory * shared;
 
 	CConnection *firstConnection;
 public: