mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Move gui locking to GUIHandler
This commit is contained in:
		| @@ -78,7 +78,6 @@ void processCommand(const std::string &message, CClient *&client); | |||||||
| extern std::queue<SDL_Event> events; | extern std::queue<SDL_Event> events; | ||||||
| extern boost::mutex eventsM; | extern boost::mutex eventsM; | ||||||
| boost::recursive_mutex * CPlayerInterface::pim = new boost::recursive_mutex; | boost::recursive_mutex * CPlayerInterface::pim = new boost::recursive_mutex; | ||||||
| CondSh<bool> CPlayerInterface::terminate_cond; |  | ||||||
|  |  | ||||||
| CPlayerInterface * LOCPLINT; | CPlayerInterface * LOCPLINT; | ||||||
|  |  | ||||||
| @@ -112,14 +111,13 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) | |||||||
| 	makingTurn = false; | 	makingTurn = false; | ||||||
| 	showingDialog = new CondSh<bool>(false); | 	showingDialog = new CondSh<bool>(false); | ||||||
| 	cingconsole = new CInGameConsole; | 	cingconsole = new CInGameConsole; | ||||||
| 	terminate_cond.set(false); | 	GH.terminate_cond.set(false); | ||||||
| 	firstCall = 1; //if loading will be overwritten in serialize | 	firstCall = 1; //if loading will be overwritten in serialize | ||||||
| 	autosaveCount = 0; | 	autosaveCount = 0; | ||||||
| 	isAutoFightOn = false; | 	isAutoFightOn = false; | ||||||
|  |  | ||||||
| 	duringMovement = false; | 	duringMovement = false; | ||||||
| 	ignoreEvents = false;	 | 	ignoreEvents = false;	 | ||||||
| 	locked = false; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| CPlayerInterface::~CPlayerInterface() | CPlayerInterface::~CPlayerInterface() | ||||||
| @@ -1590,11 +1588,13 @@ void CPlayerInterface::setSelection(const CArmedInstance * obj) | |||||||
|  |  | ||||||
| void CPlayerInterface::update() | void CPlayerInterface::update() | ||||||
| { | { | ||||||
| 	if (!locked) | 	// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request | ||||||
| 	{ | 	boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex()); | ||||||
| 		logGlobal->errorStream() << "Non synchronized update of PlayerInterface"; | 	 | ||||||
|  | 	// While mutexes were locked away we may be have stopped being the active interface	 | ||||||
|  | 	if(LOCPLINT != this) | ||||||
| 		return; | 		return; | ||||||
| 	} | 	 | ||||||
| 	//if there are any waiting dialogs, show them | 	//if there are any waiting dialogs, show them | ||||||
| 	if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get()) | 	if((howManyPeople <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get()) | ||||||
| 	{ | 	{ | ||||||
| @@ -1622,38 +1622,6 @@ void CPlayerInterface::update() | |||||||
| 		GH.drawFPSCounter(); | 		GH.drawFPSCounter(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CPlayerInterface::runLocked(std::function<void()> functor) |  | ||||||
| { |  | ||||||
| 	// Updating GUI requires locking pim mutex (that protects screen and GUI state). |  | ||||||
| 	// When ending the game, the pim mutex might be hold by other thread, |  | ||||||
| 	// that will notify us about the ending game by setting terminate_cond flag. |  | ||||||
|  |  | ||||||
| 	bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded |  | ||||||
| 	while(!terminate_cond.get() && !(acquiredTheLockOnPim = pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate |  | ||||||
| 		boost::this_thread::sleep(boost::posix_time::milliseconds(15)); |  | ||||||
|  |  | ||||||
| 	if(!acquiredTheLockOnPim) |  | ||||||
| 	{ |  | ||||||
| 		// We broke the while loop above and not because of mutex, so we must be terminating. |  | ||||||
| 		assert(terminate_cond.get()); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock. |  | ||||||
| 	boost::unique_lock<boost::recursive_mutex> un(*pim, boost::adopt_lock); |  | ||||||
|  |  | ||||||
| 	// While mutexes were locked away we may be have stopped being the active interface |  | ||||||
| 	if(LOCPLINT != this) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request |  | ||||||
| 	boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex()); |  | ||||||
|  |  | ||||||
| 	locked = true; |  | ||||||
| 	functor(); |  | ||||||
| 	locked = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int CPlayerInterface::getLastIndex( std::string namePrefix) | int CPlayerInterface::getLastIndex( std::string namePrefix) | ||||||
| { | { | ||||||
| 	using namespace boost::filesystem; | 	using namespace boost::filesystem; | ||||||
| @@ -2133,7 +2101,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul | |||||||
| 		{ | 		{ | ||||||
| 			if(adventureInt) | 			if(adventureInt) | ||||||
| 			{ | 			{ | ||||||
| 				terminate_cond.setn(true); | 				GH.terminate_cond.setn(true); | ||||||
| 				adventureInt->deactivate(); | 				adventureInt->deactivate(); | ||||||
| 				if(GH.topInt() == adventureInt) | 				if(GH.topInt() == adventureInt) | ||||||
| 					GH.popInt(adventureInt); | 					GH.popInt(adventureInt); | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  |  | ||||||
| //#include "../lib/CondSh.h" |  | ||||||
| #include "../lib/FunctionList.h" | #include "../lib/FunctionList.h" | ||||||
| #include "../lib/CGameInterface.h" | #include "../lib/CGameInterface.h" | ||||||
| #include "../lib/NetPacksBase.h" | #include "../lib/NetPacksBase.h" | ||||||
| @@ -85,7 +84,7 @@ enum | |||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Central class for managing user interface logic | /// Central class for managing user interface logic | ||||||
| class CPlayerInterface : public CGameInterface, public ILockedUpdatable | class CPlayerInterface : public CGameInterface, public IUpdateable | ||||||
| { | { | ||||||
| 	const CArmedInstance * currentSelection; | 	const CArmedInstance * currentSelection; | ||||||
| public: | public: | ||||||
| @@ -136,7 +135,6 @@ public: | |||||||
| 	} spellbookSettings; | 	} spellbookSettings; | ||||||
|  |  | ||||||
| 	void update() override; | 	void update() override; | ||||||
| 	void runLocked(std::function<void()> functor) override; |  | ||||||
| 	void initializeHeroTownList(); | 	void initializeHeroTownList(); | ||||||
| 	int getLastIndex(std::string namePrefix); | 	int getLastIndex(std::string namePrefix); | ||||||
|  |  | ||||||
| @@ -271,10 +269,6 @@ public: | |||||||
| 	CPlayerInterface(PlayerColor Player);//c-tor | 	CPlayerInterface(PlayerColor Player);//c-tor | ||||||
| 	~CPlayerInterface();//d-tor | 	~CPlayerInterface();//d-tor | ||||||
|  |  | ||||||
| 	static CondSh<bool> terminate_cond; // confirm termination |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| private: | private: | ||||||
|  |  | ||||||
| 	template <typename Handler> void serializeTempl(Handler &h, const int version); | 	template <typename Handler> void serializeTempl(Handler &h, const int version); | ||||||
| @@ -300,8 +294,6 @@ private: | |||||||
| 	bool duringMovement; | 	bool duringMovement; | ||||||
| 	bool ignoreEvents; | 	bool ignoreEvents; | ||||||
|  |  | ||||||
| 	bool locked; |  | ||||||
|  |  | ||||||
| 	void doMoveHero(const CGHeroInstance *h, CGPath path); | 	void doMoveHero(const CGHeroInstance *h, CGPath path); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -516,7 +516,6 @@ void CGPreGame::disposeGraphics() | |||||||
|  |  | ||||||
| void CGPreGame::update() | void CGPreGame::update() | ||||||
| { | { | ||||||
| 	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim); |  | ||||||
| 	if(CGP != this) //don't update if you are not a main interface | 	if(CGP != this) //don't update if you are not a main interface | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| @@ -543,12 +542,6 @@ void CGPreGame::update() | |||||||
| 		GH.drawFPSCounter(); | 		GH.drawFPSCounter(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CGPreGame::runLocked(std::function<void()> cb) |  | ||||||
| { |  | ||||||
| 	boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim); |  | ||||||
| 	cb();	 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CGPreGame::openCampaignScreen(std::string name) | void CGPreGame::openCampaignScreen(std::string name) | ||||||
| { | { | ||||||
| 	if (vstd::contains(CGPreGameConfig::get().getCampaigns().Struct(), name)) | 	if (vstd::contains(CGPreGameConfig::get().getCampaigns().Struct(), name)) | ||||||
|   | |||||||
| @@ -597,7 +597,7 @@ private: | |||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection | /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection | ||||||
| class CGPreGame : public CIntObject, public ILockedUpdatable | class CGPreGame : public CIntObject, public IUpdateable | ||||||
| { | { | ||||||
| 	void loadGraphics(); | 	void loadGraphics(); | ||||||
| 	void disposeGraphics(); | 	void disposeGraphics(); | ||||||
| @@ -611,7 +611,6 @@ public: | |||||||
|  |  | ||||||
| 	~CGPreGame(); | 	~CGPreGame(); | ||||||
| 	void update() override; | 	void update() override; | ||||||
| 	void runLocked(std::function<void()> cb) override; |  | ||||||
| 	void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER); | 	void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER); | ||||||
|  |  | ||||||
| 	void openCampaignScreen(std::string name); | 	void openCampaignScreen(std::string name); | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "StdInc.h" | #include "StdInc.h" | ||||||
| #include "CGuiHandler.h" | #include "CGuiHandler.h" | ||||||
|  | #include "../lib/CondSh.h" | ||||||
|  |  | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
|  |  | ||||||
| @@ -15,6 +16,7 @@ | |||||||
| extern std::queue<SDL_Event> events; | extern std::queue<SDL_Event> events; | ||||||
| extern boost::mutex eventsM; | extern boost::mutex eventsM; | ||||||
|  |  | ||||||
|  | CondSh<bool> CGuiHandler::terminate_cond; | ||||||
| boost::thread_specific_ptr<bool> inGuiThread; | boost::thread_specific_ptr<bool> inGuiThread; | ||||||
|  |  | ||||||
| SObjectConstruction::SObjectConstruction( CIntObject *obj ) | SObjectConstruction::SObjectConstruction( CIntObject *obj ) | ||||||
| @@ -389,12 +391,30 @@ void CGuiHandler::fakeMouseMove() | |||||||
|  |  | ||||||
| void CGuiHandler::renderFrame() | void CGuiHandler::renderFrame() | ||||||
| { | { | ||||||
| 	auto doUpdate = [this]() |  | ||||||
| 	{ | 	{ | ||||||
| 		if(nullptr != curInt) | 	// Updating GUI requires locking pim mutex (that protects screen and GUI state). | ||||||
|  | 	// During game: | ||||||
|  | 	// When ending the game, the pim mutex might be hold by other thread, | ||||||
|  | 	// that will notify us about the ending game by setting terminate_cond flag.		 | ||||||
|  | 	//in PreGame terminate_cond stay false  | ||||||
|  | 		 | ||||||
|  | 		bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded | ||||||
|  | 		while(!terminate_cond.get() && !(acquiredTheLockOnPim = CPlayerInterface::pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate | ||||||
|  | 			boost::this_thread::sleep(boost::posix_time::milliseconds(15)); | ||||||
|  |  | ||||||
|  | 		if(!acquiredTheLockOnPim) | ||||||
| 		{ | 		{ | ||||||
| 			curInt -> update(); | 			// We broke the while loop above and not because of mutex, so we must be terminating. | ||||||
|  | 			assert(terminate_cond.get()); | ||||||
|  | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock. | ||||||
|  | 		boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim, boost::adopt_lock); | ||||||
|  |  | ||||||
|  | 		if(nullptr != curInt) | ||||||
|  | 			curInt->update(); | ||||||
|  | 			 | ||||||
| 		// draw the mouse cursor and update the screen | 		// draw the mouse cursor and update the screen | ||||||
| 		CCS->curh->render(); | 		CCS->curh->render(); | ||||||
|  |  | ||||||
| @@ -402,12 +422,8 @@ void CGuiHandler::renderFrame() | |||||||
| 			logGlobal->errorStream() << __FUNCTION__ << " SDL_RenderCopy " << SDL_GetError(); | 			logGlobal->errorStream() << __FUNCTION__ << " SDL_RenderCopy " << SDL_GetError(); | ||||||
|  |  | ||||||
| 		SDL_RenderPresent(mainRenderer);					 | 		SDL_RenderPresent(mainRenderer);					 | ||||||
| 	}; | 	} | ||||||
|  |  | ||||||
| 	if(curInt) |  | ||||||
| 		curInt->runLocked(doUpdate); |  | ||||||
| 	else |  | ||||||
| 		doUpdate(); |  | ||||||
| 	 | 	 | ||||||
| 	mainFPSmng->framerateDelay(); // holds a constant FPS	 | 	mainFPSmng->framerateDelay(); // holds a constant FPS	 | ||||||
| } | } | ||||||
| @@ -423,6 +439,8 @@ CGuiHandler::CGuiHandler() | |||||||
| 	// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate | 	// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate | ||||||
| 	mainFPSmng = new CFramerateManager(48); | 	mainFPSmng = new CFramerateManager(48); | ||||||
| 	//do not init CFramerateManager here --AVS | 	//do not init CFramerateManager here --AVS | ||||||
|  | 	 | ||||||
|  | 	terminate_cond.set(false); | ||||||
| } | } | ||||||
|  |  | ||||||
| CGuiHandler::~CGuiHandler() | CGuiHandler::~CGuiHandler() | ||||||
|   | |||||||
| @@ -8,9 +8,9 @@ class CFramerateManager; | |||||||
| class CGStatusBar; | class CGStatusBar; | ||||||
| class CIntObject; | class CIntObject; | ||||||
| class IUpdateable; | class IUpdateable; | ||||||
| class ILockedUpdatable; |  | ||||||
| class IShowActivatable; | class IShowActivatable; | ||||||
| class IShowable; | class IShowable; | ||||||
|  | template <typename T> struct CondSh; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * CGuiHandler.h, part of VCMI engine |  * CGuiHandler.h, part of VCMI engine | ||||||
| @@ -72,7 +72,7 @@ public: | |||||||
| 	std::vector<IShowable*> objsToBlit; | 	std::vector<IShowable*> objsToBlit; | ||||||
|  |  | ||||||
| 	SDL_Event * current; //current event - can be set to nullptr to stop handling event | 	SDL_Event * current; //current event - can be set to nullptr to stop handling event | ||||||
| 	ILockedUpdatable *curInt; | 	IUpdateable *curInt; | ||||||
|  |  | ||||||
| 	Point lastClick; | 	Point lastClick; | ||||||
| 	unsigned lastClickTime; | 	unsigned lastClickTime; | ||||||
| @@ -109,6 +109,8 @@ public: | |||||||
| 	static bool isArrowKey(SDL_Keycode key); | 	static bool isArrowKey(SDL_Keycode key); | ||||||
| 	static bool amIGuiThread(); | 	static bool amIGuiThread(); | ||||||
| 	static void pushSDLEvent(int type, int usercode = 0); | 	static void pushSDLEvent(int type, int usercode = 0); | ||||||
|  | 	 | ||||||
|  | 	static CondSh<bool> terminate_cond; // confirm termination | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern CGuiHandler GH; //global gui handler | extern CGuiHandler GH; //global gui handler | ||||||
|   | |||||||
| @@ -38,13 +38,6 @@ public: | |||||||
| 	virtual ~IUpdateable(){}; //d-tor | 	virtual ~IUpdateable(){}; //d-tor | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ILockedUpdatable: public IUpdateable |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	virtual void runLocked(std::function<void()> cb) = 0; |  | ||||||
| 	virtual ~ILockedUpdatable(){}; //d-tor |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // Defines a show method | // Defines a show method | ||||||
| class IShowable | class IShowable | ||||||
| { | { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user