diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index a48dbe1d2..8efd2a194 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -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); + } }); } diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 774c90edd..c3b54c045 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -155,7 +155,7 @@ public: void invalidatePaths() override; - void makeTurn(); + void makeTurn() noexcept; void buildArmyIn(const CGTownInstance * t); void endTurn(); diff --git a/AI/Nullkiller2/AIGateway.cpp b/AI/Nullkiller2/AIGateway.cpp index e96f98f7f..d5e38dd84 100644 --- a/AI/Nullkiller2/AIGateway.cpp +++ b/AI/Nullkiller2/AIGateway.cpp @@ -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); + } }); } diff --git a/AI/Nullkiller2/AIGateway.h b/AI/Nullkiller2/AIGateway.h index 607771d30..955e8ffe0 100644 --- a/AI/Nullkiller2/AIGateway.h +++ b/AI/Nullkiller2/AIGateway.h @@ -155,7 +155,7 @@ public: void invalidatePaths() override; - void makeTurn(); + void makeTurn() noexcept; void buildArmyIn(const CGTownInstance * t); void endTurn(); diff --git a/AI/Nullkiller2/Engine/Nullkiller.cpp b/AI/Nullkiller2/Engine/Nullkiller.cpp index fce127d53..13a97dc35 100644 --- a/AI/Nullkiller2/Engine/Nullkiller.cpp +++ b/AI/Nullkiller2/Engine/Nullkiller.cpp @@ -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); diff --git a/lib/ConditionalWait.h b/lib/ConditionalWait.h index 8bff553c5..d318b4a66 100644 --- a/lib/ConditionalWait.h +++ b/lib/ConditionalWait.h @@ -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 interruptionRequested = false; @@ -34,7 +45,7 @@ public: bool result = interruptionRequested.exchange(false); if (result) - throw TerminationRequestedException(); + throw InterruptionRequestedException(); } void interruptThread()