1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00

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.
This commit is contained in:
Arseniy Shestakov 2017-06-04 15:59:11 +03:00
parent a2284c3209
commit 1a60c1a94b
7 changed files with 77 additions and 62 deletions

View File

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

View File

@ -260,6 +260,8 @@ int main(int argc, char** argv)
opts.add_options() opts.add_options()
("help,h", "display help and exit") ("help,h", "display help and exit")
("version,v", "display version information 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") ("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
("start", po::value<bfs::path>(), "starts game from saved StartInfo file") ("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
("testmap", po::value<std::string>(), "") ("testmap", po::value<std::string>(), "")
@ -345,6 +347,9 @@ int main(int argc, char** argv)
session["headless"].Bool() = true; session["headless"].Bool() = true;
session["onlyai"].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 // Init special testing settings
session["serverport"].Integer() = vm.count("serverport") ? vm["serverport"].as<si64>() : 0; session["serverport"].Integer() = vm.count("serverport") ? vm["serverport"].as<si64>() : 0;

View File

@ -41,9 +41,7 @@
#include "CMT.h" #include "CMT.h"
extern std::string NAME; extern std::string NAME;
#ifndef VCMI_ANDROID #ifdef VCMI_ANDROID
namespace intpr = boost::interprocess;
#else
#include "lib/CAndroidVMHelper.h" #include "lib/CAndroidVMHelper.h"
#endif #endif
@ -1019,13 +1017,7 @@ void CServerHandler::waitForServer()
#ifndef VCMI_ANDROID #ifndef VCMI_ANDROID
if(shared) if(shared)
{ shared->sr->waitTillReady();
intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
while(!shared->sr->ready)
{
shared->sr->cond.wait(slock);
}
}
#else #else
logNetwork->infoStream() << "waiting for server"; logNetwork->infoStream() << "waiting for server";
while (!androidTestServerReadyFlag.load()) while (!androidTestServerReadyFlag.load())
@ -1042,15 +1034,7 @@ void CServerHandler::waitForServer()
CConnection * CServerHandler::connectToServer() CConnection * CServerHandler::connectToServer()
{ {
#ifndef VCMI_ANDROID
if(shared)
{
if(!shared->sr->ready)
waitForServer();
}
#else
waitForServer(); waitForServer();
#endif
th.update(); //put breakpoint here to attach to server before it does something stupid 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; serverThread = nullptr;
shared = nullptr; shared = nullptr;
verbose = true; verbose = true;
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
#ifndef VCMI_ANDROID #ifndef VCMI_ANDROID
if(DO_NOT_START_SERVER) if(DO_NOT_START_SERVER || settings["session"]["disable-shm"].Bool())
return; 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 try
{ {
shared = new SharedMem(); shared = new SharedMemory(sharedMemoryName, true);
} }
catch(...) catch(...)
{ {
@ -1115,11 +1105,17 @@ void CServerHandler::callServer()
#ifndef VCMI_ANDROID #ifndef VCMI_ANDROID
setThreadName("CServerHandler::callServer"); setThreadName("CServerHandler::callServer");
const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string(); 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() + " --port=" + getDefaultPortStr()
+ " --run-by-client" + " --run-by-client"
+ (shared ? " --use-shm" : "") + " --uuid=" + uuid;
+ " > \"" + logName + '\"'; 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()); int result = std::system(comm.c_str());
if (result == 0) if (result == 0)

View File

@ -28,7 +28,7 @@ class CGameInterface;
class CConnection; class CConnection;
class CCallback; class CCallback;
struct BattleAction; struct BattleAction;
struct SharedMem; struct SharedMemory;
class CClient; class CClient;
class CScriptingModule; class CScriptingModule;
struct CPathsInfo; struct CPathsInfo;
@ -46,7 +46,8 @@ public:
CStopWatch th; CStopWatch th;
boost::thread *serverThread; //thread that called system to run server 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 bool verbose; //whether to print log msgs
//functions setting up local server //functions setting up local server

View File

@ -20,8 +20,8 @@ struct ServerReady
{ {
bool ready; bool ready;
uint16_t port; //ui16? uint16_t port; //ui16?
boost::interprocess::interprocess_mutex mutex; boost::interprocess::interprocess_mutex mutex;
boost::interprocess::interprocess_condition cond; boost::interprocess::interprocess_condition cond;
ServerReady() ServerReady()
{ {
@ -29,7 +29,16 @@ struct ServerReady
port = 0; port = 0;
} }
void setToTrueAndNotify(uint16_t Port) void waitTillReady()
{
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> slock(mutex);
while(!ready)
{
cond.wait(slock);
}
}
void setToReadyAndNotify(const uint16_t Port)
{ {
{ {
boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex); boost::unique_lock<boost::interprocess::interprocess_mutex> lock(mutex);
@ -40,22 +49,33 @@ struct ServerReady
} }
}; };
struct SharedMem struct SharedMemory
{ {
const char * name;
boost::interprocess::shared_memory_object smo; boost::interprocess::shared_memory_object smo;
boost::interprocess::mapped_region *mr; boost::interprocess::mapped_region * mr;
ServerReady *sr; ServerReady * sr;
SharedMem() //c-tor SharedMemory(std::string Name, bool initialize = false)
:smo(boost::interprocess::open_or_create,"vcmi_memory",boost::interprocess::read_write) : 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)); smo.truncate(sizeof(ServerReady));
mr = new boost::interprocess::mapped_region(smo,boost::interprocess::read_write); 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; delete mr;
boost::interprocess::shared_memory_object::remove("vcmi_memory"); boost::interprocess::shared_memory_object::remove(name);
} }
}; };

View File

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

View File

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