Skip to content

Commit

Permalink
Fix a deadlock when loading/reloading scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 5, 2025
1 parent 1b8da08 commit 8358c55
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/Mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ std::optional<std::string> Mods::on_initialize() const {


std::optional<std::string> Mods::on_initialize_d3d_thread() const {
std::scoped_lock _{g_framework->get_hook_monitor_mutex()};
auto do_not_hook_d3d = g_framework->acquire_do_not_hook_d3d();

utility::Config cfg{ (REFramework::get_persistent_dir() / REFrameworkConfig::REFRAMEWORK_CONFIG_NAME).string() };

Expand Down
16 changes: 12 additions & 4 deletions src/REFramework.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,25 @@ using namespace std::literals;
std::unique_ptr<REFramework> g_framework{};

void REFramework::hook_monitor() {
if (m_do_not_hook_d3d_count.load() > 0) {
// Wait until nothing important is happening
m_last_present_time = std::chrono::steady_clock::now() + std::chrono::seconds(5);
m_last_chance_time = std::chrono::steady_clock::now() + std::chrono::seconds(1);
m_has_last_chance = true;
return;
}

if (!m_hook_monitor_mutex.try_lock()) {
// If this happens then we can assume execution is going as planned
// so we can just reset the times so we dont break something
m_last_present_time = std::chrono::steady_clock::now() + std::chrono::seconds(5);
m_last_chance_time = std::chrono::steady_clock::now() + std::chrono::seconds(1);
m_has_last_chance = true;
} else {
m_hook_monitor_mutex.unlock();
return;
}

std::scoped_lock _{ m_hook_monitor_mutex };
// Take ownership of the mutex with adopt_lock
std::lock_guard _{ m_hook_monitor_mutex, std::adopt_lock };

if (g_framework == nullptr) {
return;
Expand Down Expand Up @@ -2018,7 +2026,7 @@ bool REFramework::first_frame_initialize() {
return is_init_ok;
}

std::scoped_lock _{get_hook_monitor_mutex()};
auto do_not_hook_d3d = acquire_do_not_hook_d3d();

spdlog::info("Running first frame D3D initialization of mods...");

Expand Down
24 changes: 24 additions & 0 deletions src/REFramework.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ class RETypes;
class REFramework {
private:
void hook_monitor();
std::atomic<uint32_t> m_do_not_hook_d3d_count{0};

public:
struct DoNotHook {
DoNotHook(std::atomic<uint32_t>& count) : m_count(count) {
++m_count;
}

~DoNotHook() {
--m_count;
}

private:
std::atomic<uint32_t>& m_count;
};

DoNotHook acquire_do_not_hook_d3d() {
return DoNotHook{m_do_not_hook_d3d_count};
}


public:
REFramework(HMODULE reframework_module);
Expand Down Expand Up @@ -121,6 +141,10 @@ class REFramework {
return m_hook_monitor_mutex;
}

auto& get_startup_mutex() {
return m_startup_mutex;
}

void set_font_size(int size) {
if (m_font_size != size) {
m_font_size = size;
Expand Down
3 changes: 2 additions & 1 deletion src/mods/ScriptRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,8 @@ void ScriptRunner::spew_error(const std::string& p) {


void ScriptRunner::reset_scripts() {
std::scoped_lock __{ g_framework->get_hook_monitor_mutex() }; // Stops D3D monitor from attempting to re-hook during long script startups
auto do_not_hook_d3d = g_framework->acquire_do_not_hook_d3d();

std::scoped_lock _{ m_access_mutex };

{
Expand Down

0 comments on commit 8358c55

Please sign in to comment.