Skip to content

fix(memory): Fix memory leak for audio events when pausing the game#2731

Open
Caball009 wants to merge 2 commits into
TheSuperHackers:mainfrom
Caball009:fix_memory_leak_audio_requests
Open

fix(memory): Fix memory leak for audio events when pausing the game#2731
Caball009 wants to merge 2 commits into
TheSuperHackers:mainfrom
Caball009:fix_memory_leak_audio_requests

Conversation

@Caball009
Copy link
Copy Markdown

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:

//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.

There's one exception where the ownership of the audio event is taken away from the audio request:

@Caball009 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
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 19, 2026

Greptile Summary

This PR fixes a memory leak in the audio system that occurred when the game was paused while audio events were queued in the audio request container. The AudioRequest destructor previously did not free the owning m_pendingEvent pointer, so requests purged during pause were leaked.

  • Destructor fix: ~AudioRequest() now deletes m_pendingEvent when m_usePendingEvent is true, covering the pause-time purge path and any other early-exit path.
  • Ownership transfer: releasePendingEvent() atomically clears m_usePendingEvent and nulls the pointer before returning it, ensuring the destructor won't double-free once ownership moves to PlayingAudio::m_audioEventRTS.
  • Refactored playAudioEvent: Now receives the full AudioRequest* instead of a raw AudioEventRTS*, allowing the three assignment sites (Streaming, 3D sample, 2D sample) to call releasePendingEvent() at exactly the point of ownership transfer.

Confidence Score: 5/5

Safe to merge — the fix is narrowly scoped, ownership transfer is atomic and guarded against double-free, and all three play paths correctly call releasePendingEvent() exactly once.

The destructor addition and releasePendingEvent() implementation are straightforward and correct. The refactored playAudioEvent uses the local event alias only before ownership transfer and switches to audio->m_audioEventRTS exclusively afterward. Early-return paths (e.g., !info) are now also covered because the destructor handles cleanup when releasePendingEvent() was never called.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/AudioRequest.h Adds releasePendingEvent() declaration; existing struct layout unchanged. Clean change.
Core/GameEngine/Source/Common/Audio/AudioRequest.cpp Implements the destructor fix and releasePendingEvent(). Destructor guards with m_usePendingEvent before deleting, and releasePendingEvent() clears the flag and pointer atomically before returning to prevent double-free.
Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h Updates playAudioEvent signature from AudioEventRTS* to AudioRequest* to enable ownership tracking within the function.
Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Refactors playAudioEvent to accept AudioRequest*. Transfers ownership via releasePendingEvent() exactly once in each code path (Streaming, 3D sample, 2D sample). Uses audio->m_audioEventRTS exclusively for all post-release accesses; the local event alias is only read before transfer.

Sequence Diagram

sequenceDiagram
    participant ARC as AudioRequestContainer
    participant PAE as processRequest
    participant PAF as playAudioEvent
    participant AR as AudioRequest
    participant PA as PlayingAudio

    ARC->>PAE: iterate queued requests on pause
    PAE->>PAF: playAudioEvent(req) [AR_Play]
    PAF->>AR: "read req->m_pendingEvent (local alias)"
    PAF->>AR: getAudioEventInfo() — early return?
    alt "info == nullptr"
        PAF-->>PAE: return (req destructor frees event)
        PAE->>AR: ~AudioRequest() — delete m_pendingEvent
    else valid info
        PAF->>AR: releasePendingEvent()
        AR-->>PAF: "returns AudioEventRTS* (clears m_usePendingEvent)"
        PAF->>PA: "audio->m_audioEventRTS = released event"
        PA-->>PAF: owns event henceforth
    end
Loading

Reviews (5): Last reviewed commit: "Implemented 'releasePendingEvent' member..." | Re-trigger Greptile

Comment thread Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Outdated
Comment thread Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Outdated
@Caball009 Caball009 force-pushed the fix_memory_leak_audio_requests branch from 057637e to 977bd8b Compare May 19, 2026 21:15
Copy link
Copy Markdown

@xezon xezon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good.

@Caball009 Caball009 force-pushed the fix_memory_leak_audio_requests branch from 977bd8b to afb337c Compare May 19, 2026 22:15
@Caball009
Copy link
Copy Markdown
Author

Rebased to include the fix for the CI Replay checker.

Comment thread Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp Outdated
@Caball009 Caball009 force-pushed the fix_memory_leak_audio_requests branch from 9e09418 to 08f2330 Compare May 20, 2026 17:01
Comment thread Core/GameEngine/Source/Common/Audio/AudioRequest.cpp Outdated

// Put this on here, so that the audio event RTS will be cleaned up regardless.
audio->m_audioEventRTS = event;
audio->m_audioEventRTS = req->releasePendingEvent();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also write audio->m_audioEventRTS = event = req->releasePendingEvent(); and just keep using event everywhere.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of the assignment to event?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's optional, but just a tiny bit more robust if releasePendingEvent was changed and would suddenly introduce a new pointer.

@Caball009 Caball009 force-pushed the fix_memory_leak_audio_requests branch from 08f2330 to 130a39f Compare May 20, 2026 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Gen Relates to Generals Memory Is memory related Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants