fix(headless): Fix use-after-free crash when replay ends in headless mode in debug build#2219
fix(headless): Fix use-after-free crash when replay ends in headless mode in debug build#2219bobtista wants to merge 2 commits intoTheSuperHackers:mainfrom
Conversation
Greptile Overview
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | Changed from ParticleSystemManager::reset() to update() for headless mode particle cleanup, simplified comment |
xezon
left a comment
There was a problem hiding this comment.
Fast shutdown is a common way to exit apps, but it is not a good sign to do this to avoid problems here. Where is the problem exactly?
Did a bunch of testing and found the crash was:
Pushed two fixes - first, we use update() instead of reset(), which leaves active particles intact. Second, I was getting memory leak debug asserts even with ignoreAsserts enabled. It seems that: The second fix is to set |
0fc66f8 to
3198cff
Compare
|
After the changes in #2235 to use and search for ParticleID's instead of holding pointers, is this valid anymore? |
|
Is this change still relevant? Edit: Looks like it still is relevant. |
| DX8Wrapper_IsWindowed = false; | ||
| } | ||
| else if (initializeAppWindows(hInstance, nCmdShow, TheGlobalData->m_windowed) == false) | ||
| return exitcode; |
GeneralsMD/Code/Main/WinMain.cpp
Outdated
| if(!TheGlobalData->m_headless && initializeAppWindows(hInstance, nCmdShow, TheGlobalData->m_windowed) == false) | ||
| // TheSuperHackers @fix bobtista 02/03/2026 Set DX8Wrapper_IsWindowed to false in headless | ||
| // mode so that ignoringAsserts() works correctly throughout the entire process lifetime, | ||
| // including during shutdown after TheGlobalData has been destroyed. |
There was a problem hiding this comment.
This looks like a change that could also have been separate of the particles change.
There was a problem hiding this comment.
Yes it looks to be a separate change, right?
| // the particles accumulate and slow things down a lot and can even cause a crash on | ||
| // longer replays. | ||
| TheParticleSystemManager->reset(); | ||
| // TheSuperHackers @fix bobtista 02/02/2026 Use update() instead of reset() to avoid |
There was a problem hiding this comment.
Please combine this comment with the previous comment. Provide a rationale why update is preferred over reset, other than the dangling pointers whch should no longer be problem after #2235
| // (W3DTruckDraw, W3DTankDraw, etc.) hold raw pointers to particle systems. When those | ||
| // DrawModules are later destroyed during game shutdown, they crash accessing freed memory. | ||
| // update() only cleans up finished particle systems, leaving active ones intact. | ||
| TheParticleSystemManager->update(); |
There was a problem hiding this comment.
I suspect the downside of this update is that it will require more CPU processing than just resetting. But otherwise for logical correctness this seems like the better thing to do.
…articleSystemManager::update() instead of reset()
7a3e413 to
3d7c95f
Compare
| // TheSuperHackers @info helmutbuhler 03/05/2025 bobtista 02/02/2026 | ||
| // Update particles to prevent accumulation in headless mode. update() has slightly more | ||
| // CPU overhead than reset() but is semantically correct - particles finish naturally. | ||
| TheParticleSystemManager->update(); |
There was a problem hiding this comment.
Comment says update() has "slightly more CPU overhead" but this depends on active particle count - with many particles, update() processes each one while reset() just bulk-deletes. Consider removing "slightly more CPU overhead" to avoid confusion.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp
Line: 786:789
Comment:
Comment says `update()` has "slightly more CPU overhead" but this depends on active particle count - with many particles, `update()` processes each one while `reset()` just bulk-deletes. Consider removing "slightly more CPU overhead" to avoid confusion.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
Summary
ParticleSystemManager::update()instead ofreset(). Usingupdate()has slightly more CPU overhead thanreset()but is semantically correct - particles finish their lifecycle naturally.Test plan
-replay test.rep -headless)