Merge remote-tracking branch 'suokko/main_thread_ownership_for_df_ai' into develop

develop
lethosor 2018-07-06 16:37:06 -04:00
commit aa76b92662
2 changed files with 131 additions and 41 deletions

@ -97,13 +97,36 @@ using df::global::world;
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL); static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string *pfocus = NULL);
size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder); size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder);
//! mainThreadSuspend keeps the main DF thread suspended from Core::Init to namespace DFHack {
struct MainThread {
//! MainThread::suspend keeps the main DF thread suspended from Core::Init to
//! thread exit. //! thread exit.
template<typename M> static CoreSuspenderBase& suspend() {
static std::unique_lock<M>& mainThreadSuspend(M& mutex) { static thread_local CoreSuspenderBase lock(std::defer_lock);
static thread_local std::unique_lock<M> lock(mutex, std::defer_lock);
return lock; return lock;
} }
};
}
CoreSuspendReleaseMain::CoreSuspendReleaseMain()
{
MainThread::suspend().unlock();
}
CoreSuspendReleaseMain::~CoreSuspendReleaseMain()
{
MainThread::suspend().lock();
}
CoreSuspendClaimMain::CoreSuspendClaimMain()
{
MainThread::suspend().lock();
}
CoreSuspendClaimMain::~CoreSuspendClaimMain()
{
MainThread::suspend().unlock();
}
struct Core::Private struct Core::Private
{ {
@ -1456,8 +1479,8 @@ void fIOthread(void * iodata)
Core::~Core() Core::~Core()
{ {
if (mainThreadSuspend(CoreSuspendMutex).owns_lock()) if (MainThread::suspend().owns_lock())
mainThreadSuspend(CoreSuspendMutex).unlock(); MainThread::suspend().unlock();
if (d->hotkeythread.joinable()) { if (d->hotkeythread.joinable()) {
std::lock_guard<std::mutex> lock(HotkeyMutex); std::lock_guard<std::mutex> lock(HotkeyMutex);
@ -1546,7 +1569,7 @@ bool Core::Init()
// Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown // Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown
// Core::Update will temporary unlock when there is any commands queued // Core::Update will temporary unlock when there is any commands queued
mainThreadSuspend(CoreSuspendMutex).lock(); MainThread::suspend().lock();
// Re-route stdout and stderr again - DF seems to set up stdout and // Re-route stdout and stderr again - DF seems to set up stdout and
// stderr.txt on Windows as of 0.43.05. Also, log before switching files to // stderr.txt on Windows as of 0.43.05. Also, log before switching files to
@ -2007,8 +2030,6 @@ int Core::Update()
// Pretend this thread has suspended the core in the usual way, // Pretend this thread has suspended the core in the usual way,
// and run various processing hooks. // and run various processing hooks.
{ {
CoreSuspendClaimer suspend(true);
// Initialize the core // Initialize the core
bool first_update = false; bool first_update = false;
@ -2025,7 +2046,7 @@ int Core::Update()
} }
// Let all commands run that require CoreSuspender // Let all commands run that require CoreSuspender
CoreWakeup.wait(mainThreadSuspend(CoreSuspendMutex), CoreWakeup.wait(MainThread::suspend(),
[this]() -> bool {return this->toolCount.load() == 0;}); [this]() -> bool {return this->toolCount.load() == 0;});
return 0; return 0;
@ -2247,8 +2268,8 @@ int Core::Shutdown ( void )
errorstate = 1; errorstate = 1;
// Make sure we release main thread if this is called from main thread // Make sure we release main thread if this is called from main thread
if (mainThreadSuspend(CoreSuspendMutex).owns_lock()) if (MainThread::suspend().owns_lock())
mainThreadSuspend(CoreSuspendMutex).unlock(); MainThread::suspend().unlock();
// Make sure the console thread shutdowns before clean up to avoid any // Make sure the console thread shutdowns before clean up to avoid any
// unlikely data races. // unlikely data races.

@ -299,14 +299,54 @@ namespace DFHack
friend class CoreService; friend class CoreService;
friend class ServerConnection; friend class ServerConnection;
friend class CoreSuspender; friend class CoreSuspender;
friend class CoreSuspenderBase;
friend struct CoreSuspendClaimMain;
friend struct CoreSuspendReleaseMain;
ServerMain *server; ServerMain *server;
}; };
template<typename Derived> class CoreSuspenderBase : protected std::unique_lock<std::recursive_mutex> {
struct ToolIncrement { protected:
ToolIncrement(std::atomic<size_t>& toolCount) { using parent_t = std::unique_lock<std::recursive_mutex>;
toolCount += 1; std::thread::id tid;
CoreSuspenderBase(std::defer_lock_t d) : CoreSuspenderBase{&Core::getInstance(), d} {}
CoreSuspenderBase(Core* core, std::defer_lock_t) :
/* Lock the core */
parent_t{core->CoreSuspendMutex,std::defer_lock},
/* Mark this thread to be the core owner */
tid{}
{}
public:
void lock()
{
auto& core = Core::getInstance();
parent_t::lock();
tid = core.ownerThread.exchange(std::this_thread::get_id(),
std::memory_order_acquire);
}
void unlock()
{
auto& core = Core::getInstance();
/* Restore core owner to previous value */
core.ownerThread.store(tid, std::memory_order_release);
if (tid == std::thread::id{})
Lua::Core::Reset(core.getConsole(), "suspend");
parent_t::unlock();
}
bool owns_lock() const noexcept
{
return parent_t::owns_lock();
} }
~CoreSuspenderBase() {
if (owns_lock())
unlock();
}
friend class MainThread;
}; };
/*! /*!
@ -332,37 +372,66 @@ namespace DFHack
* no more tools are queued trying to acquire the * no more tools are queued trying to acquire the
* Core::CoreSuspenderMutex. * Core::CoreSuspenderMutex.
*/ */
class CoreSuspender : protected ToolIncrement<CoreSuspender>, class CoreSuspender : public CoreSuspenderBase {
public std::unique_lock<std::recursive_mutex> { using parent_t = CoreSuspenderBase;
using parent_t = std::unique_lock<std::recursive_mutex>;
Core *core;
std::thread::id tid;
public: public:
CoreSuspender() : CoreSuspender(&Core::getInstance()) { } CoreSuspender() : CoreSuspender{&Core::getInstance()} { }
CoreSuspender(bool) : CoreSuspender(&Core::getInstance()) { } CoreSuspender(std::defer_lock_t d) : CoreSuspender{&Core::getInstance(),d} { }
CoreSuspender(Core* core, bool) : CoreSuspender(core) { } CoreSuspender(bool) : CoreSuspender{&Core::getInstance()} { }
CoreSuspender(Core* core, bool) : CoreSuspender{core} { }
CoreSuspender(Core* core) : CoreSuspender(Core* core) :
/* Increment the wait count */ CoreSuspenderBase{core, std::defer_lock}
ToolIncrement{core->toolCount}, {
/* Lock the core */ lock();
parent_t{core->CoreSuspendMutex}, }
core{core}, CoreSuspender(Core* core, std::defer_lock_t) :
/* Mark this thread to be the core owner */ CoreSuspenderBase{core, std::defer_lock}
tid{core->ownerThread.exchange(std::this_thread::get_id())}
{} {}
~CoreSuspender() {
/* Restore core owner to previous value */ void lock()
core->ownerThread.store(tid); {
if (tid == std::thread::id{}) auto& core = Core::getInstance();
Lua::Core::Reset(core->getConsole(), "suspend"); core.toolCount.fetch_add(1, std::memory_order_relaxed);
parent_t::lock();
}
void unlock()
{
auto& core = Core::getInstance();
parent_t::unlock();
/* Notify core to continue when all queued tools have completed /* Notify core to continue when all queued tools have completed
* 0 = None wants to own the core * 0 = None wants to own the core
* 1+ = There are tools waiting core access * 1+ = There are tools waiting core access
* fetch_add returns old value before subtraction * fetch_add returns old value before subtraction
*/ */
if (core->toolCount.fetch_add(-1) == 1) if (core.toolCount.fetch_add(-1, std::memory_order_relaxed) == 1)
core->CoreWakeup.notify_one(); core.CoreWakeup.notify_one();
} }
~CoreSuspender() {
if (owns_lock())
unlock();
}
};
/*!
* Temporary release main thread ownership to allow alternative thread
* implement DF logic thread loop
*/
struct DFHACK_EXPORT CoreSuspendReleaseMain {
CoreSuspendReleaseMain();
~CoreSuspendReleaseMain();
};
/*!
* Temporary claim main thread ownership. This allows caller to call
* Core::Update from a different thread than original DF logic thread if
* logic thread has released main thread ownership with
* CoreSuspendReleaseMain
*/
struct DFHACK_EXPORT CoreSuspendClaimMain {
CoreSuspendClaimMain();
~CoreSuspendClaimMain();
}; };
using CoreSuspendClaimer = CoreSuspender; using CoreSuspendClaimer = CoreSuspender;