1
0
mirror of https://github.com/vcmi/vcmi.git synced 2026-04-26 20:02:20 +02:00
Files
vcmi/lib/ConditionalWait.h
Ivan Savenko af801c1824 Try to fix possible freezes on AI actions
While I am not 100% sure about the cause, it seems that following
scenarios might happen right now:

- Network thread is shut down due to catching
TerminationRequestedException that was intended to only terminate AI
turn
- throwing an exception in AI thread may lead to a freeze (and not to a
crash) since TBB catches all crashes to later re-throw them in caller
thread (which does not happens in VCMI due to way AI logic is set up)
- Also, AI makeTurn thread right now has catch-all block in it, meaning
that any bugs caused by unexpected exception throw are silenced

List of changes

- functions that are passed to TBB in AI are now marked as `noexcept`.
So any exception not caught and not processed by our code will cause
std::terminate and not be lost inside TBB
- separate TerminationRequestedException which now only requests app
termination, and InterruptionRequestedException that requests AI to
terminate its turn

Expected results:
- Network thread should no longer unexpectedly terminate due to AI
logic. Instead it might result in a crash, but with correct call stack
- Any exception thrown by AI should now result in a clean crash and not
in AI freeze

Sure, this means introduction of potential crashes, but tracking them
down should be easier since they are now detected in place, not at some
point later. iOS/macOS crash reports and Google Play reports should help
with tracking them down (if any such crashes do appear)
2026-02-20 14:16:43 +02:00

112 lines
1.7 KiB
C++

/*
* ConditionalWait.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include <condition_variable>
VCMI_LIB_NAMESPACE_BEGIN
class TerminationRequestedException : public std::exception
{
public:
using exception::exception;
const char* what() const noexcept override
{
return "Thread termination requested";
}
};
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;
public:
void interruptionPoint()
{
bool result = interruptionRequested.exchange(false);
if (result)
throw InterruptionRequestedException();
}
void interruptThread()
{
interruptionRequested.store(true);
}
void reset()
{
interruptionRequested.store(false);
}
};
class ConditionalWait
{
bool isBusyValue = false;
bool isTerminating = false;
std::condition_variable cond;
std::mutex mx;
void set(bool value)
{
std::unique_lock lock(mx);
isBusyValue = value;
}
public:
ConditionalWait() = default;
void setBusy()
{
set(true);
}
void setFree()
{
set(false);
cond.notify_all();
}
void requestTermination()
{
isTerminating = true;
setFree();
}
bool isBusy()
{
std::unique_lock lock(mx);
return isBusyValue;
}
void waitWhileBusy()
{
std::unique_lock un(mx);
cond.wait(un, [this](){ return !isBusyValue;});
if (isTerminating)
throw TerminationRequestedException();
}
};
VCMI_LIB_NAMESPACE_END