You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//Get rid of PLAY audio requests when pausing audio.
std::list<AudioRequest*>::iterator ait;
for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); /* empty */)
{
AudioRequest *req = (*ait);
if( req && req->m_request == AR_Play )
{
deleteInstance(req);
ait = m_audioRequests.erase(ait);
}
else
{
ait++;
}
}
AudioRequest::m_pendingEvent can be an owning raw pointer, though, which the destructor of AudioRequest should delete when needed. It currently doesn't, which is why the leak happens.
Caball009
added
Minor
Severity: Minor < Major < Critical < Blocker
Gen
Relates to Generals
ZH
Relates to Zero Hour
Memory
Is memory related
Fix
Is fixing something, but is not user facing
labels
May 19, 2026
This PR fixes a memory leak that occurred when the game was paused while audio play requests were queued in m_audioRequests. The root cause was that AudioRequest::~AudioRequest() did not delete m_pendingEvent even though the request could be the owning holder of that pointer.
Adds a destructor body to AudioRequest that conditionally deletes m_pendingEvent when m_usePendingEvent is true, and introduces releasePendingEvent() to transfer ownership away from the request before destruction.
Refactors playAudioEvent to accept AudioRequest* instead of a raw AudioEventRTS*, calling releasePendingEvent() at each of the three assignment sites where ownership moves to a PlayingAudio slot, leaving cleanup to the destructor on all early-exit paths (e.g., !info return).
Confidence Score: 5/5
Safe to merge — the fix correctly scopes ownership of the audio event to the AudioRequest destructor and uses a well-defined release idiom to transfer it to PlayingAudio at exactly one point per execution path.
The destructor addition and releasePendingEvent() method are straightforward and provably correct: every code path through playAudioEvent either leaves m_usePendingEvent true (destructor cleans up) or calls releasePendingEvent() exactly once before transferring the pointer to PlayingAudio (which cleans it up via releasePlayingAudio). No double-free or use-after-free paths were found.
Adds destructor that deletes m_pendingEvent when owned, and releasePendingEvent() for safe ownership transfer — both correctly conditioned on m_usePendingEvent.
Core/GameEngine/Include/Common/AudioRequest.h
Declares releasePendingEvent() in the AudioRequest struct; no structural issues.
playAudioEvent signature changed to take AudioRequest*; releasePendingEvent() called at exactly one site per execution path (stream, 3D, 2D), leaving the destructor responsible for all early-exit cleanup.
Updates playAudioEvent declaration to match new AudioRequest* parameter — straightforward signature update.
Sequence Diagram
sequenceDiagram
participant PRL as processRequestList
participant AR as AudioRequest
participant PAE as playAudioEvent
participant PA as PlayingAudio
PRL->>AR: deleteInstance(req) [pause path]
Note over AR: destructor sees m_usePendingEvent==true, deletes m_pendingEvent
PRL->>PAE: playAudioEvent(req) [normal path]
PAE->>PAE: "event = req->m_pendingEvent (read only)"
alt early return due to missing info
PAE-->>PRL: return
Note over AR: destructor deletes m_pendingEvent since m_usePendingEvent still true
else stream or 3D or 2D sample branch
PAE->>AR: releasePendingEvent()
Note over AR: sets m_usePendingEvent=false and m_pendingEvent=nullptr
AR-->>PAE: "AudioEventRTS* (ownership transferred)"
PAE->>PA: "audio->m_audioEventRTS = event"
Note over PA: releasePlayingAudio frees it later
end
PRL->>AR: deleteInstance(req)
Note over AR: destructor sees m_usePendingEvent==false, no double-delete
The reason will be displayed to describe this comment to others. Learn more.
Looking good
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FixIs fixing something, but is not user facingGenRelates to GeneralsMemoryIs memory relatedMinorSeverity: Minor < Major < Critical < BlockerZHRelates to Zero Hour
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pausing the game leaks audio events that were in the audio request container at the time. This PR fixes that.
This code is called to get rid of some of the audio requests when pausing the game:
GeneralsGameCode/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp
Lines 587 to 601 in 2219f63
AudioRequest::m_pendingEventcan be an owning raw pointer, though, which the destructor ofAudioRequestshould delete when needed. It currently doesn't, which is why the leak happens.GeneralsGameCode/Core/GameEngine/Include/Common/AudioRequest.h
Line 51 in 2219f63
There's one exception where the ownership of the audio event is taken away from the audio request:
GeneralsGameCode/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp
Line 2238 in 2219f63