bugfix(milesaudiomanager): Use reference counted DynamicAudioEventRTS class in AudioRequest and PlayingAudio to prevent race conditions when sharing audio event data in MilesAudioManager::startNextLoop()#2774
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp | Core race-condition fix: startNextLoop now signals m_rerequestOnNextUpdate (written before InterlockedCompareExchange) and defers AudioRequest creation to the main thread in processPlayingList, ensuring both PlayingAudio and the new AudioRequest share the same ref-counted event safely. |
| Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h | Replaces m_cleanupAudioEventRTS flag with m_rerequestOnNextUpdate flag and upgrades m_audioEventRTS to RefCountPtr<DynamicAudioEventRTS>; new rerequestPlayingAudio helper declared. |
| Core/GameEngine/Include/Common/AudioEventRTS.h | DynamicAudioEventRTS flattens its nested m_event member into direct inheritance from AudioEventRTS and adds RefCountClass for shared ownership; constructors and Delete_This override look correct. |
| Core/GameEngine/Include/Common/AudioRequest.h | Removes the union-based m_pendingEvent/m_handleToInteractOn layout and replaces AudioEventRTS* with RefCountPtr<DynamicAudioEventRTS>; m_usePendingEvent flag eliminated correctly. |
| Core/Libraries/Source/WWVegas/WWLib/refcount.h | Adds RefCountMTClass (thread-safe, atomic ref counting) but it is not used by DynamicAudioEventRTS; constitutes dead code in the current PR. |
| Core/Libraries/Source/WWVegas/WWLib/refcount.cpp | Implements RefCountMTClass::Add_Ref and Release_Ref using InterlockedIncrement/InterlockedDecrement — correct implementation, mirrors RefCountClass but with atomic ops. |
| Core/GameEngine/Source/Common/Audio/GameAudio.cpp | Migrates addAudioEvent to use RefCountPtr and removes releaseAudioEventRTS; allocateAudioRequest simplified by removing the useAudioEvent parameter. |
| GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h | AudioArray members updated to RefCountPtr<DynamicAudioEventRTS>, manual new/delete replaced by RefCountPtr; copy and assignment operators look correct. |
| GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp | All m_ambientSound->m_event. accesses updated to m_ambientSound-> (direct inheritance), manual deleteInstance replaced with RefCountPtr::Clear(); straightforward mechanical update. |
| Dependencies/Utility/Utility/interlocked_adapter.h | Adds InterlockedIncrement/InterlockedDecrement wrappers needed by RefCountMTClass on older Win32 SDK targets where the signatures differ. |
Sequence Diagram
sequenceDiagram
participant MT as MSS Timer Thread
participant Main as Main Thread
participant PA as PlayingAudio
participant AR as AudioRequest
participant Ev as DynamicAudioEventRTS (RefCountPtr)
Note over PA,Ev: rc=1 (owned by PlayingAudio)
MT->>PA: notifyOfAudioCompletion()
PA->>MT: "startNextLoop() — delay > threshold"
MT->>PA: "m_rerequestOnNextUpdate = true"
MT->>PA: InterlockedCompareExchange(status, PS_Stopping)
Note over MT: Timer thread done — no AudioRequest created here
Main->>PA: processPlayingList() — sees m_rerequestOnNextUpdate
Main->>AR: rerequestPlayingAudio() — copies m_audioEventRTS (Add_Ref)
Note over Ev: rc=2 (PlayingAudio + AudioRequest)
Main->>PA: "m_rerequestOnNextUpdate = false"
Main->>PA: "status == PS_Stopping → stopPlayingAudio()"
Main->>PA: releasePlayingAudioInListIfStopped() → delete PlayingAudio (Release_Ref)
Note over Ev: rc=1 (AudioRequest only — no dangling pointer)
Main->>AR: processRequestList() → playAudioEvent()
AR->>Ev: assign to new PlayingAudio.m_audioEventRTS (Add_Ref)
Note over Ev: rc=2 (AudioRequest + new PlayingAudio)
Main->>AR: deleteInstance(req) (Release_Ref)
Note over Ev: rc=1 (new PlayingAudio only)
Reviews (9): Last reviewed commit: "fixup! bugfix(milesaudiomanager): Preven..." | Re-trigger Greptile
c9bfbb0 to
049a95b
Compare
|
I revisited the implementation and added new fixup commits to simplify it, because I noticed that we can avoid adding the deferred audio requests container by using a new flag in The RefCountMTClass is now no longer used, but we can keep it anyway for future use cases. |
2ee51b3 to
22f58cd
Compare
…in PlayingAudio when handing it over to a new AudioRequest after a call to MilesAudioManager::startNextLoop() (#2774)
22f58cd to
1d3692e
Compare
…entRTS in PlayingAudio when handing it over to a new AudioRequest after a call to MilesAudioManager::startNextLoop() (#2774)
730b739 to
cb3b9b4
Compare
|
Polished. Ready for review. |
Merge with Rebase
This change has 3 commits to work towards fixing race conditions in
MilesAudioManagerconcerning a sharedAudioEventRTSinstance in classesAudioRequestandPlayingAudio.The first commit implements a new
RefCountMTClasswhich is fundamentally identical toRefCountClass, except it has a thread safe counter and all the debug functionality is omitted.The second commit adds the
RefCountMTClassRefCountClasstoDynamicAudioEventRTSto allow for shared ownership. All existing users ofDynamicAudioEventRTSaccomodate it and will now useRefCountPtrfor automatic reference counting.The third commits replaces
AudioEventRTS*withRefCountPtr<DynamicAudioEventRTS>inAudioRequestandPlayingAudioto allow sharing the audio event data between them. This is needed, because ownership will be shared in functionMilesAudioManager::startNextLoop(orMilesAudioManager::stopPlayingAudio), where previouslyAudioRequestwas given the sole authority to delete theAudioEventRTSwhilePlayingAudiostill kept a pointer to it. Now both classes need to release their reference count before the audio event data is deleted.This likely was also a problem in retail, because
AudioEventRTSis heap allocated, not pool allocated.TODO