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/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

View File

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

View File

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

View File

@ -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

View File

@ -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)
{
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);
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);
}
};

View File

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

View File

@ -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: