diff --git a/Global.h b/Global.h index 37726f9ac..c26892b7e 100644 --- a/Global.h +++ b/Global.h @@ -185,6 +185,9 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); #include #include #include +#include +#include +#include #ifndef M_PI # define M_PI 3.14159265358979323846 diff --git a/client/CMT.cpp b/client/CMT.cpp index e686a6196..63c95453b 100644 --- a/client/CMT.cpp +++ b/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(), "runs game in duel mode (battle-only") ("start", po::value(), "starts game from saved StartInfo file") ("testmap", po::value(), "") @@ -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() : 0; diff --git a/client/Client.cpp b/client/Client.cpp index 33706cc24..0c429cb08 100644 --- a/client/Client.cpp +++ b/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 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) diff --git a/client/Client.h b/client/Client.h index 3431e6f8d..7e2cbf45f 100644 --- a/client/Client.h +++ b/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 diff --git a/lib/Interprocess.h b/lib/Interprocess.h index 8798aa580..cca2c80b7 100644 --- a/lib/Interprocess.h +++ b/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 slock(mutex); + while(!ready) + { + cond.wait(slock); + } + } + + void setToReadyAndNotify(const uint16_t Port) { { - boost::unique_lock lock(mutex); + boost::unique_lock 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(mr->get_address()); }; - ~SharedMem() //d-tor + + ~SharedMemory() { delete mr; - boost::interprocess::shared_memory_object::remove("vcmi_memory"); + boost::interprocess::shared_memory_object::remove(name); } }; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index a40911aa2..1542cf3b6 100644 --- a/server/CVCMIServer.cpp +++ b/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 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(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(); } + 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(), "") + ("enable-shm-uuid", "use UUID for shared memory identifier") + ("enable-shm", "enable usage of shared memory") ("port", po::value(), "port at which server will listen to connections from client") ("resultsFile", po::value()->default_value("./results.txt"), "file to which the battle result will be appended. Used only in the DUEL mode."); diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index 478072905..8374cc789 100644 --- a/server/CVCMIServer.h +++ b/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: