fix(milesaudiomanager): Prevent multithread crashing in MilesAudioManager#2718
fix(milesaudiomanager): Prevent multithread crashing in MilesAudioManager#2718xezon wants to merge 2 commits into
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp | Core threading fix: deferred two-phase (stop/release) design with per-list critical sections; removes stoppedAudio list, splits releasePlayingAudio into stopPlayingAudio+releasePlayingAudio, and applies InterlockedCompareExchange for status transitions. |
| Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h | Adds PS_Stopping state, four CriticalSectionClass members, new stopPlayingAudio/fadePlayingAudio/findActiveMusic helpers, removes processStoppedList and getMusicTrackName; PlayingAudio gains m_fade and Short m_framesFaded fields with a static_assert on m_status size. |
| Dependencies/Utility/Utility/interlocked_adapter.h | New header providing an inline InterlockedCompareExchange overload for legacy MSVC less than 1300 that casts through PVOID; uses pragma once and is correctly marked inline. |
| Core/GameEngine/Include/Common/GameAudio.h | nextMusicTrack/prevMusicTrack signatures changed from void to AsciiString return; getMusicTrackName pure virtual removed. |
| Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp | Updated MSG_META_DEMO_MUSIC_NEXT/PREV_TRACK handlers to use the AsciiString return value from nextMusicTrack/prevMusicTrack instead of a separate getMusicTrackName call. |
| GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp | Same CommandXlat update as the Generals variant: uses AsciiString return from nextMusicTrack/prevMusicTrack. |
Sequence Diagram
sequenceDiagram
participant MT as Main Thread
participant MSS as MSS Timer Thread
participant L as CriticalSectionCS
Note over MT: processPlayingList Phase 1 unlocked
MT->>MT: for each item call stopPlayingAudio if PS_Stopping
Note over MSS: EOS callback fires
MSS->>L: acquire m_playingSoundsCS
L-->>MSS: lock acquired
MSS->>MSS: findPlayingAudioFrom compare handle
MSS->>L: release lock
MSS->>MSS: notifyOfAudioCompletion CAS PS_Playing to PS_Stopping
Note over MT: processPlayingList Phase 2 locked
MT->>L: acquire m_playingSoundsCS
L-->>MT: lock acquired
MT->>MT: erase PS_Stopped items releasePlayingAudio
MT->>L: release lock
Reviews (5): Last reviewed commit: "fix(milesaudiomanager): Prevent multithr..." | Re-trigger Greptile
a02315e to
b3fe6c7
Compare
b3fe6c7 to
01d9dbd
Compare
01d9dbd to
025e5e7
Compare
| InterlockedCompareExchange(reinterpret_cast<volatile long*>(&release->m_status), PS_Stopping, PS_Playing); | ||
| InterlockedCompareExchange(reinterpret_cast<volatile long*>(&release->m_status), PS_Stopped, PS_Stopping); |
There was a problem hiding this comment.
Double atomic operation looks suspicious.
There was a problem hiding this comment.
It was written this way to make sure these states advance in order atomically.
If we did release->m_status = PS_Stopped here and the other thread tried to set it to PS_Stopping, then PS_Stopped may not win the race.
|
Could you rebase this to get the CI replay check working again? |
025e5e7 to
ed9b1a0
Compare
Done |
Merge with Rebase
This change is split into 2 commits. The first cleans up some junk in Miles Audio Manager. And the second applies everything necessary for the fix.
It fixes a rare data race in
MilesAudioManager, by preventing accessing potentially invalidated iterators inMilesAudioManager::findPlayingAudioFrom, which runs on the MSS Timer thread. This potentially is no issue in regular builds, because an invalidated iterator might still be usable when the memory was not reused.Note that it NOT possible to simply defer the event processing coming in
MilesAudioManager::notifyOfAudioCompletionto Main thread, because it wants to continue looping sounds immediately, and any delays will introduce audible stutter.Note that the stop playing audio step is often seperated from the release playing audio step, because it is illegal to call into Miles while holding the new critical section due Miles holding its own internal mutex during callbacks to
MilesAudioManager::notifyOfAudioCompletionand Miles API functions.TODO