From 63f983e5780a4dd2d9905ea8b3a07013dc07984b Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 3 Dec 2024 11:49:36 -0600 Subject: [PATCH] fix semantics for conditional locking this switches the core lock to a recursive timed mutex, uses a `try_lock_for` in `CoreSuspenderBase` and changes `help_helper` and `get_commands` over to using conditional locks to avoid a hang in the event that a core lock cannot be acquired in these methods i also moved some repeated code into protected/private methods in both `CoreSuspenderBase` and `CoreSuspender` to mitigate DRY, which also massively improves readability h/t to @NicksWorld for their help in figuring out the required semantics Co-Authored-By: Nicholas McDaniel <29175042+NicksWorld@users.noreply.github.com> --- library/Core.cpp | 10 +++---- library/include/Core.h | 64 +++++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 2babae976c..14b035bea9 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -439,10 +439,9 @@ bool is_builtin(color_ostream &con, const std::string &command) { } void get_commands(color_ostream &con, std::vector &commands) { - // FIXME: this should be a conditional lock but that functionality doesn't work yet - CoreSuspender suspend{}; + CoreSuspender suspend{std::defer_lock}; - if (!suspend.owns_lock()) { + if (!suspend.try_lock()) { con.printerr("Cannot acquire core lock in helpdb.get_commands\n"); commands.clear(); return; @@ -646,10 +645,9 @@ static std::string sc_event_name (state_change_event id) { } void help_helper(color_ostream &con, const std::string &entry_name) { - // FIXME: this should be a conditional lock but that functionality doesn't work yet - CoreSuspender suspend{ }; + CoreSuspender suspend{ std::defer_lock }; - if (!suspend.owns_lock()) { + if (!suspend.try_lock()) { con.printerr("Failed Lua call to helpdb.help (could not acquire core lock).\n"); return; } diff --git a/library/include/Core.h b/library/include/Core.h index 9671cb1892..765f4adc36 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -340,7 +340,7 @@ namespace DFHack * \sa DFHack::CoreSuspender * \{ */ - std::recursive_mutex CoreSuspendMutex; + std::recursive_timed_mutex CoreSuspendMutex; std::condition_variable_any CoreWakeup; std::atomic ownerThread; std::atomic toolCount; @@ -372,23 +372,14 @@ namespace DFHack public: void lock() { - auto& core = Core::getInstance(); parent_t::lock(); - tid = core.ownerThread.exchange(std::this_thread::get_id(), - std::memory_order_acquire); - lock_count++; + complete_lock(); } bool try_lock() { - auto& core = Core::getInstance(); - bool locked = parent_t::try_lock(); - if (locked) - { - tid = core.ownerThread.exchange(std::this_thread::get_id(), - std::memory_order_acquire); - lock_count++; - } + bool locked = parent_t::try_lock_for(std::chrono::milliseconds(100)); + if (locked) complete_lock(); return locked; } @@ -413,6 +404,16 @@ namespace DFHack while (lock_count > 0) unlock(); } + + private: + void complete_lock() + { + auto& core = Core::getInstance(); + tid = core.ownerThread.exchange(std::this_thread::get_id(), + std::memory_order_acquire); + lock_count++; + } + friend class MainThread; }; @@ -457,37 +458,48 @@ namespace DFHack void lock() { - auto& core = Core::getInstance(); - core.toolCount.fetch_add(1, std::memory_order_relaxed); + inc_tool_count(); parent_t::lock(); } bool try_lock() { - auto& core = Core::getInstance(); - bool locked = parent_t::try_lock(); - if (locked) - core.toolCount.fetch_add(1, std::memory_order_relaxed); - return locked; + inc_tool_count(); + if (parent_t::try_lock()) + return true; + dec_tool_count(); + return false; } void unlock() { - auto& core = Core::getInstance(); parent_t::unlock(); + dec_tool_count(); + } + + ~CoreSuspender() { + while (lock_count > 0) + unlock(); + } + + protected: + void inc_tool_count() + { + auto& core = Core::getInstance(); + core.toolCount.fetch_add(1, std::memory_order_relaxed); + } + + void dec_tool_count() + { /* Notify core to continue when all queued tools have completed * 0 = None wants to own the core * 1+ = There are tools waiting core access * fetch_add returns old value before subtraction */ + auto& core = Core::getInstance(); if (core.toolCount.fetch_add(-1, std::memory_order_relaxed) == 1) core.CoreWakeup.notify_one(); } - - ~CoreSuspender() { - while (lock_count > 0) - unlock(); - } }; /*!