浏览代码

Better handling of AI shutdown

Ivan Savenko 9 月之前
父节点
当前提交
219eea86ff
共有 6 个文件被更改,包括 46 次插入18 次删除
  1. 9 1
      AI/Nullkiller/AIGateway.cpp
  2. 1 1
      CCallback.cpp
  3. 3 0
      client/CServerHandler.cpp
  4. 14 13
      client/Client.cpp
  5. 18 2
      client/Client.h
  6. 1 1
      client/NetPacksClient.cpp

+ 9 - 1
AI/Nullkiller/AIGateway.cpp

@@ -884,7 +884,15 @@ void AIGateway::makeTurn()
 	}
 #endif
 
-	endTurn();
+	try
+	{
+		endTurn();
+	}
+	catch (const TerminationRequestedException & e)
+	{
+		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
+		return;
+	}
 }
 
 void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)

+ 1 - 1
CCallback.cpp

@@ -260,7 +260,7 @@ int CBattleCallback::sendRequest(const CPackForServer & request)
 	{
 		logGlobal->trace("We'll wait till request %d is answered.\n", requestID);
 		auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting);
-		CClient::waitingRequest.waitWhileContains(requestID);
+		cl->waitingRequest.waitWhileContains(requestID);
 	}
 	return requestID;
 }

+ 3 - 0
client/CServerHandler.cpp

@@ -653,6 +653,8 @@ void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victo
 
 void CServerHandler::endGameplay()
 {
+	client->finishGameplay();
+
 	// Game is ending
 	// Tell the network thread to reach a stable state
 	sendClientDisconnecting();
@@ -671,6 +673,7 @@ void CServerHandler::endGameplay()
 
 void CServerHandler::restartGameplay()
 {
+	client->finishGameplay();
 	client->endGame();
 	client.reset();
 

+ 14 - 13
client/Client.cpp

@@ -46,8 +46,6 @@
 #include "lib/CAndroidVMHelper.h"
 #endif
 
-ThreadSafeVector<int> CClient::waitingRequest;
-
 CPlayerEnvironment::CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_)
 	: player(player_),
 	cl(cl_),
@@ -181,25 +179,28 @@ void CClient::endNetwork()
 	}
 }
 
-void CClient::endGame()
+void CClient::finishGameplay()
 {
-#if SCRIPTING_ENABLED
-	clientScripts.reset();
-#endif
+	waitingRequest.requestTermination();
 
 	//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
 	for(auto & i : playerint)
 		i.second->finish();
+}
 
-	{
-		logNetwork->info("Ending current game!");
-		removeGUI();
+void CClient::endGame()
+{
+#if SCRIPTING_ENABLED
+	clientScripts.reset();
+#endif
 
-		GAME->setMapInstance(nullptr);
-		vstd::clear_pointer(gs);
+	logNetwork->info("Ending current game!");
+	removeGUI();
 
-		logNetwork->info("Deleted mapHandler and gameState.");
-	}
+	GAME->setMapInstance(nullptr);
+	vstd::clear_pointer(gs);
+
+	logNetwork->info("Deleted mapHandler and gameState.");
 
 	CPlayerInterface::battleInt.reset();
 	playerint.clear();

+ 18 - 2
client/Client.h

@@ -13,6 +13,8 @@
 #include <vcmi/Environment.h>
 
 #include "../lib/IGameCallback.h"
+#include "../lib/ConditionalWait.h"
+
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -50,8 +52,15 @@ class ThreadSafeVector
 	std::vector<T> items;
 	std::mutex mx;
 	std::condition_variable cond;
+	std::atomic<bool> isTerminating = false;
 
 public:
+	void requestTermination()
+	{
+		isTerminating = true;
+		clear();
+	}
+
 	void clear()
 	{
 		TLock lock(mx);
@@ -61,6 +70,8 @@ public:
 
 	void pushBack(const T & item)
 	{
+		assert(!isTerminating);
+
 		TLock lock(mx);
 		items.push_back(item);
 		cond.notify_all();
@@ -68,14 +79,18 @@ public:
 
 	void waitWhileContains(const T & item)
 	{
-		//FIXME: should throw exception on destruction
 		TLock lock(mx);
 		while(vstd::contains(items, item))
 			cond.wait(lock);
+
+		if (isTerminating)
+			throw TerminationRequestedException();
 	}
 
 	bool tryRemovingElement(const T & item) //returns false if element was not present
 	{
+		assert(!isTerminating);
+
 		TLock lock(mx);
 		auto itr = vstd::find(items, item);
 		if(itr == items.end()) //not in container
@@ -129,6 +144,7 @@ public:
 
 	void save(const std::string & fname);
 	void endNetwork();
+	void finishGameplay();
 	void endGame();
 
 	void initMapHandler();
@@ -139,7 +155,7 @@ public:
 	void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
 	void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
 
-	static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
+	ThreadSafeVector<int> waitingRequest;
 
 	void handlePack(CPackForClient & pack); //applies the given pack and deletes it
 	int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request

+ 1 - 1
client/NetPacksClient.cpp

@@ -882,7 +882,7 @@ void ApplyClientNetPackVisitor::visitEndAction(EndAction & pack)
 void ApplyClientNetPackVisitor::visitPackageApplied(PackageApplied & pack)
 {
 	callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::requestRealized, &pack);
-	if(!CClient::waitingRequest.tryRemovingElement(pack.requestID))
+	if(!cl.waitingRequest.tryRemovingElement(pack.requestID))
 		logNetwork->warn("Surprising server message! PackageApplied for unknown requestID!");
 }