Merge pull request #6934 from IvanSavenko/ai_freeze_fix

Try to fix possible freezes on AI actions
This commit is contained in:
Ivan Savenko
2026-02-20 20:56:22 +02:00
committed by GitHub
6 changed files with 58 additions and 58 deletions
+16 -22
View File
@@ -608,7 +608,7 @@ void AIGateway::yourTurn(QueryID queryID)
nullkiller->makingTurnInterrupption.reset();
asyncTasks->run([this]()
asyncTasks->run([this]() noexcept
{
ScopedThreadName guard("NKAI::AIGateway::makingTurn");
makeTurn();
@@ -850,7 +850,7 @@ bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
return upgraded;
}
void AIGateway::makeTurn()
void AIGateway::makeTurn() noexcept
{
MAKING_TURN;
@@ -877,10 +877,8 @@ void AIGateway::makeTurn()
}
}
#if NKAI_TRACE_LEVEL == 0
try
{
#endif
nullkiller->makeTurn();
//for debug purpose
@@ -889,28 +887,17 @@ void AIGateway::makeTurn()
if (h->movementPointsRemaining())
logAi->info("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
}
#if NKAI_TRACE_LEVEL == 0
}
catch (const TerminationRequestedException &)
{
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
return;
}
catch (const std::exception & e)
{
logAi->debug("Making turn thread has caught an exception: %s", e.what());
}
#endif
try
{
endTurn();
}
catch (const TerminationRequestedException &)
catch (const InterruptionRequestedException &)
{
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
return;
}
catch (const TerminationRequestedException &)
{
logAi->debug("Making turn thread has been terminated. We'll end without calling endTurn");
}
}
void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
@@ -1632,12 +1619,19 @@ void AIGateway::executeActionAsync(const std::string & description, const std::f
if (!asyncTasks)
throw std::runtime_error("Attempt to execute task on shut down AI state!");
asyncTasks->run([this, description, whatToDo]()
asyncTasks->run([this, description, whatToDo]() noexcept
{
ScopedThreadName guard("NKAI::AIGateway::" + description);
SET_GLOBAL_STATE(this);
std::shared_lock gsLock(CGameState::mutex);
whatToDo();
try
{
whatToDo();
}
catch (const TerminationRequestedException &)
{
logAi->debug("%s thread has been terminated. We'll end it immediately", description);
}
});
}
+1 -1
View File
@@ -155,7 +155,7 @@ public:
void invalidatePaths() override;
void makeTurn();
void makeTurn() noexcept;
void buildArmyIn(const CGTownInstance * t);
void endTurn();
+27 -33
View File
@@ -531,7 +531,7 @@ void AIGateway::yourTurn(QueryID queryID)
nullkiller->makingTurnInterruption.reset();
asyncTasks->run([this]()
asyncTasks->run([this]() noexcept
{
ScopedThreadName guard("NK2AI::AIGateway::makingTurn");
status.waitTillFree();
@@ -764,18 +764,18 @@ bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
return upgraded;
}
void AIGateway::makeTurn()
void AIGateway::makeTurn() noexcept
{
auto day = cc->getDate(Date::DAY);
logAi->info("Player %d (%s) starting turn, day %d", playerID, playerID.toString(), day);
std::shared_lock gsLock(CGameState::mutex);
cheatMapReveal(nullkiller);
memorizeVisitableObjs(nullkiller->memory, nullkiller->dangerHitMap, playerID, cc);
memorizeRevisitableObjs(nullkiller->memory, playerID, cc);
try
{
auto day = cc->getDate(Date::DAY);
logAi->info("Player %d (%s) starting turn, day %d", playerID, playerID.toString(), day);
std::shared_lock gsLock(CGameState::mutex);
cheatMapReveal(nullkiller);
memorizeVisitableObjs(nullkiller->memory, nullkiller->dangerHitMap, playerID, cc);
memorizeRevisitableObjs(nullkiller->memory, playerID, cc);
const auto start = std::chrono::high_resolution_clock::now();
nullkiller->makeTurn();
const auto timeElapsedMs = timeElapsed(start);
@@ -789,24 +789,17 @@ void AIGateway::makeTurn()
if (h->movementPointsRemaining())
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
}
}
catch (const TerminationRequestedException &)
{
logAi->debug("Making turn thread has been interrupted while nullkiller->makeTurn(). We'll end without calling endTurn.");
return;
}
catch (const std::exception & e)
{
logAi->error("Making turn thread has caught an exception: %s", e.what());
}
try
{
endTurn();
}
catch (const InterruptionRequestedException &)
{
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
return;
}
catch (const TerminationRequestedException &)
{
logAi->debug("Making turn thread has been interrupted endTurn().");
logAi->debug("Making turn thread has been terminated. We'll end without calling endTurn");
}
}
@@ -1361,14 +1354,8 @@ void AIGateway::finish()
if (asyncTasks)
{
try {
asyncTasks->wait();
asyncTasks.reset();
}
catch (const TerminationRequestedException &)
{
// ignore, tbb caught this exception from task and propagated it to our thread
}
asyncTasks->wait();
asyncTasks.reset();
}
}
@@ -1377,11 +1364,18 @@ void AIGateway::executeActionAsync(const std::string & description, const std::f
if (!asyncTasks)
throw std::runtime_error("Attempt to execute task on shut down AI state!");
asyncTasks->run([description, whatToDo]()
asyncTasks->run([description, whatToDo]() noexcept
{
ScopedThreadName guard("NK2AI::AIGateway::" + description);
std::shared_lock gsLock(CGameState::mutex);
whatToDo();
try
{
whatToDo();
}
catch (const TerminationRequestedException &)
{
logAi->debug("%s thread has been terminated. We'll end it immediately", description);
}
});
}
+1 -1
View File
@@ -155,7 +155,7 @@ public:
void invalidatePaths() override;
void makeTurn();
void makeTurn() noexcept;
void buildArmyIn(const CGTownInstance * t);
void endTurn();
+1
View File
@@ -490,6 +490,7 @@ void Nullkiller::makeTurn()
{
logAi->info(
"Pass %d: Heroes can still move but goal %s has too low priority %f. Increasing to ScanDepth::ALL_FULL",
pass,
taskDescription,
selectedTask->priority);
+12 -1
View File
@@ -24,6 +24,17 @@ public:
}
};
class InterruptionRequestedException : public std::exception
{
public:
using exception::exception;
const char* what() const noexcept override
{
return "Thread termination requested";
}
};
class ThreadInterruption
{
std::atomic<bool> interruptionRequested = false;
@@ -34,7 +45,7 @@ public:
bool result = interruptionRequested.exchange(false);
if (result)
throw TerminationRequestedException();
throw InterruptionRequestedException();
}
void interruptThread()