Skip to content

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

Open
macjabeth wants to merge 11 commits intoersh1:masterfrom
macjabeth:fix-bad-function-call-crash
Open

Fix std::bad_function_call crash and eliminate HUD task queue data race; add tests, CI, and DX improvements#12
macjabeth wants to merge 11 commits intoersh1:masterfrom
macjabeth:fix-bad-function-call-crash

Conversation

@macjabeth
Copy link
Copy Markdown

@macjabeth macjabeth commented Sep 26, 2025

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.

This commit fixes a crash that occurs when empty std::function objects
are invoked in the task queue. The crash manifests as an unhandled
std::bad_function_call exception.

The issue occurs when tasks are added to the queue but the function
object becomes invalid before execution (e.g., due to lambda capture
lifetime issues or uninitialized functions).

Changes:
- Add null check before invoking std::function in HUDHandler::Process
- Prevents crash when encountering empty/invalid function objects
- Maintains existing functionality for valid functions

Crash signature:
- Exception: 0xE06D7363 (C++ exception)
- Type: std::bad_function_call
- Module: TrueHUD.dll

This fix resolves crashes reported with Actor Info Bars and Boss Bars
when used with certain follower frameworks.
@GreyAScaler
Copy link
Copy Markdown

You're claiming to fix a std::bad_function_call but provide zero evidence this exception actually occurs. Where are the crash logs showing this specific exception? The testing section is completely unbelievable; you verified this works with "various follower frameworks", yet provide no reproduction steps, no specific mod versions, no actual testing methodology.

This reads like AI-generated content pattern-matched from generic C++ advice. Any competent developer working with std::function would already have this basic null check in place, and the idea that this safety measure was missing and somehow made it through testing is implausible.

Your description lacks any actual context from the TrueHUD codebase. "Line 55" means nothing without showing the surrounding code. The absolute confidence in your testing claims while providing zero verifiable evidence is the biggest red flag. Either provide actual crash logs demonstrating the std::bad_function_call or stop wasting maintainers' time with fabricated fixes.

Ensures thread-safe task processing in HUDHandler and TrueHUDAPI by draining the task queues under lock and processing them outside the lock.
This resolves potential data races when accessing shared resources from different threads.

Allows callers to override the `SKSE_SUPPORT_XBYAK` option through CMake. This gives downstream packagers greater control over which third-party libraries are used.

Guards `REL::Module::reset()` with a preprocessor definition for compatibility with standard CommonLibSSE-NG builds.
@macjabeth
Copy link
Copy Markdown
Author

You're claiming to fix a std::bad_function_call but provide zero evidence this exception actually occurs. Where are the crash logs showing this specific exception? The testing section is completely unbelievable; you verified this works with "various follower frameworks", yet provide no reproduction steps, no specific mod versions, no actual testing methodology.

This reads like AI-generated content pattern-matched from generic C++ advice. Any competent developer working with std::function would already have this basic null check in place, and the idea that this safety measure was missing and somehow made it through testing is implausible.

Your description lacks any actual context from the TrueHUD codebase. "Line 55" means nothing without showing the surrounding code. The absolute confidence in your testing claims while providing zero verifiable evidence is the biggest red flag. Either provide actual crash logs demonstrating the std::bad_function_call or stop wasting maintainers' time with fabricated fixes.

Hi Grey - crash logs and reproduction steps were already provided in our Discord discussion here - the same thread where you insisted this fix wouldn't work before I actually tested it. Built the DLL locally, replaced it, retested the exact same crash scenario. Zero crashes. Maybe try the solution before dismissing it next time.

@macjabeth macjabeth changed the title Fix std::bad_function_call crash in HUDHandler::Process Fix std::bad_function_call crash and eliminate HUD task queue data race Sep 26, 2025
@macjabeth macjabeth changed the title Fix std::bad_function_call crash and eliminate HUD task queue data race Fix std::bad_function_call crash and eliminate HUD task queue data race; add tests, CI, and DX improvements Sep 26, 2025
macjabeth and others added 3 commits September 26, 2025 23:13
…ate management; add optional debugger wait and hooks installation logging
…eplace USERPROFILE path with HOME and pre-create cache dir
@GreyAScaler
Copy link
Copy Markdown

GreyAScaler commented Sep 27, 2025

I don't have discord. This is Github, and you do not even know C++. It's pretty clear you're lying and covering your ass. None of the changes you've made actually help or fix anything, and the limitation on 24882db causes more bugs. I got secondhand embarrassment reading through them. Stop using AI to pretend to know how to code.

@macjabeth
Copy link
Copy Markdown
Author

I don't have discord. This is Github, and you do not even know C++. It's pretty clear you're lying and covering your ass. None of the changes you've made actually help or fix anything, and the limitation on 24882db causes more bugs. I got secondhand embarrassment reading through them. Stop using AI to pretend to know how to code.

I've provided working code with tests that demonstrate the fix. The PR is ready for technical review. If you have specific concerns about the implementation, please point to the exact lines of code and suggest improvements. Though, looking at your own GitHub track record and code contributions - 1 commit back in 2021, and another in 2015 - that seems unlikely. If you want to learn how to code and make positive contributions to GitHub and open source projects, I'm sure AI could help you! Take care now.

@GreyAScaler
Copy link
Copy Markdown

My concerns are not with the syntax of your code, but with its fundamental logic and your demonstrable lack of foundational knowledge. A PR that replaces a correct 'process all items' loop with a hard cap, guaranteeing a memory leak under load, is sabotage. Your ad hominem regarding my commit history is noted, and it perfectly illustrates that you have no technical ground to stand on. I would never touch AI, especially not whatever you used to write all of this garbage. You're completely logically bankrupt.

@macjabeth
Copy link
Copy Markdown
Author

@GreyAScaler Uh huh... well listen, I'm just gonna drop this here for the viewing pleasure of anyone following along (since I suspect you'd probably edit or delete the comment after this anyway):

image

You cited a CI workflow commit as causing "more bugs". That's like blaming a GitHub README for game crashes. CI doesn't ship with the DLL, friend.

You haven't provided a single line reference as requested, just vague accusations about memory leaks that don't exist and your weird moral stance about AI while contributing nothing yourself. The fact that you're so desperate to sabotage a crash fix that benefits actual users, all because your ego got bruised in Discord after having the same tantrum, perfectly illustrates what you're really about. We both know this isn't about code quality.

The maintainer and anyone else will see through this performative nonsense. If you have legitimate technical concerns, cite the specific lines. Otherwise, stop wasting everyone's time with personal vendettas dressed up as code review.

- Fix HUDHandler: Move EventResult type alias to public scope to resolve access issues
- Fix main.cpp: Use RUNTIME_SSE_LATEST constant and improve SKSEPlugin_Version initialisation
- Fix variable shadowing in HUDHandler.cpp and InfoBarBase.cpp
- Streamline CMakePresets.json: Remove Debug presets, keep only Release configuration
- Update CI workflow: Build Release artifacts instead of Debug, add verification step
- Improve runtime compatibility: Support both SSE 1.5.97 and latest AE versions
@GreyAScaler
Copy link
Copy Markdown

Try again without the AI LOL, I'm not giving your little bot more fuel to fuck things up further. Thankfully the author will never use these, bud bud. I don't need to prove myself to hot garbage :)

@macjabeth
Copy link
Copy Markdown
Author

LOL, I don't need to prove myself to hot garbage :)

Sounds like someone with no technical arguments left, only seething impotence and a bruised ego.

The maintainer can take it from here. 👍

@AndbGame
Copy link
Copy Markdown

AndbGame commented Oct 7, 2025

I would like to get a test build for it :) , I just caught an infinity loop in processing '_taskQueue'

But I'm afraid I need to spend some effort debugging what happened here, which I'm not ready for yet.

But if I somehow manage to reproduce it, maybe I would test it in more detail (although it's possible that this is some kind of extreme case that I won't be able to reproduce anymore :))


image

some description for screenshot

ProcessDelegates() -> AddActorInfoBar() -> _view->Invoke("_root.TrueHUD.AddInfoBarWidget", &obj, &arg, 1); -> skyrim.exe ... -> (infinity loop)

- Add deferred-add mechanism for actor/boss info bars to prevent
  std::unordered_map insertions during iteration
- Introduce _isUpdatingWidgets guard flag to detect update loop
- Add _pendingActorInfoBarAdds and _pendingBossInfoBarAdds queues
- Implement FlushPendingAdds() to process queued additions after iteration
- Move UpdateBossQueue() to execute after boss bar iteration completes
- Prevents iterator invalidation that caused crashes/infinite loops

Fixes issue where ProcessDelegates() -> AddActorInfoBar() caused
re-entrant map mutations during Update() iteration.

All unit tests passing. Ready for in-game testing.
@macjabeth
Copy link
Copy Markdown
Author

@AndbGame Much appreciated for testing the build and reporting that here. 🙏 I've added a potential fix if you have the capacity to test it out and see if it fixes the issue. Otherwise if you're able to clarify anything around when and where the crash occurred and what you were doing, I could try to replicate it on my end. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants