TrueHUD icon indicating copy to clipboard operation
TrueHUD copied to clipboard

Fix std::bad_function_call crash and eliminate HUD task queue data race; add tests, CI, and DX improvements

Open macjabeth opened this issue 3 months ago • 10 comments

Summary

Fixes a critical std::bad_function_call crash in HUD task processing and improves thread-safety across the task queue system. Also adds unit tests, Windows CI workflow, and build system improvements.

Problem

Users reported crashes when Actor Info Bars or Boss Bars are enabled, particularly with follower frameworks:

  • Exception: std::bad_function_call in TrueHUD.dll
  • Cause: Task queue accessed concurrently from multiple threads without synchronization, leading to empty or corrupted std::function objects

Solution

Core Fix: Thread-Safe Task Processing

Implemented drain-and-process pattern for both HUDHandler and WidgetBase:

  • Drain under lock: Swap entire queue into local storage under mutex
  • Process outside lock: Execute tasks without holding mutex, preventing deadlocks and contention
  • Guard invocation: Check std::function validity before calling to prevent crashes
// New pattern in HUDHandler::Process and WidgetBase::ProcessDelegates
std::queue<Task> localTasks;
{
    Locker locker(_lock);
    std::swap(localTasks, _taskQueue);
}
while (!localTasks.empty()) {
    auto task = std::move(localTasks.front());
    localTasks.pop();
    if (task) {  // Guard against empty functions
        task();
    }
}

Additional Fixes

  • Menu visibility handling: Refactored to avoid ControlMap::contextPriorityStack crashes; now uses direct menu state checks
  • Null checks: Added safety guards for ObjectRefHandle::get() results before dereferencing
  • Build system: Default CompiledPluginsPath, allow SKSE_SUPPORT_XBYAK override, ensure deploy directory exists

Testing

  • Unit tests added (Catch2) validating:
    • Empty task handling without crashes
    • FIFO ordering within drained batches
    • Reentrancy (tasks added during processing run next cycle)
    • Concurrent producer/consumer scenarios
  • CI workflow (Windows): Build Debug + run tests on every push
  • Manual testing performed within large modlists that use TrueHUD to confirm functionality.

Files Changed

Core (thread-safety fixes):

  • src/HUDHandler.cpp|.h - Drain-and-process pattern, menu visibility refactor
  • src/TrueHUDAPI.h - Same pattern for WidgetBase::ProcessDelegates
  • src/Widgets/InfoBarBase.cpp - Null check improvements
  • src/main.cpp - Conditional REL::Module::reset() for test builds

Build/Infrastructure:

  • CMakeLists.txt, src/CMakeLists.txt - Build system improvements
  • vcpkg.json - Add Catch2 dependency
  • .gitignore - Simplify patterns

Tests:

  • tests/widgetbase_tests.cpp - WidgetBase delegate processing tests
  • tests/hudhandler_process_tests.cpp - HUDHandler task queue tests

CI:

  • .github/workflows/ci.yml - Windows build + test automation

Backward Compatibility

No breaking changes to TrueHUD API or existing functionality.

macjabeth avatar Sep 26 '25 06:09 macjabeth