mirror of
https://github.com/vcmi/vcmi.git
synced 2026-04-26 20:02:20 +02:00
af801c1824
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)
112 lines
1.7 KiB
C++
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
|