From 4aab76ed760ce521501e36b07962ba18d4a73e34 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 17 Jun 2026 19:47:21 -0400
Subject: [PATCH] mixer: Lock tracks while mixing them.
They previously locked while pulling data from them, but there is a lot more
state involved with mixing after that--including a user callback--that were
running unlocked, and could explode if the app changes various track state
during that time.
Fixes #869.
---
src/SDL_mixer.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/SDL_mixer.c b/src/SDL_mixer.c
index a89d6cdb..31cba15a 100644
--- a/src/SDL_mixer.c
+++ b/src/SDL_mixer.c
@@ -579,6 +579,7 @@ static void SDLCALL MixerCallback(void *userdata, SDL_AudioStream *stream, int a
int group_bytes = 0;
MIX_Track *next_track = NULL;
for (MIX_Track *track = group->tracks; track; track = next_track) {
+ LockTrack(track);
next_track = track->group_next; // this won't save you from a callback going totally rogue, but it'll deal with the current track leaving the group.
track->currently_inuse = true;
@@ -616,7 +617,10 @@ static void SDLCALL MixerCallback(void *userdata, SDL_AudioStream *stream, int a
}
track->currently_inuse = false;
- if (track->destroy_requested) { // callback asked to destroy the track while we were still using it.
+ const bool destroy_requested = track->destroy_requested; // save this off just in case, but if the callback destroyed the track, _nothing_ else should touch it once this unlocks.
+ UnlockTrack(track);
+
+ if (destroy_requested) { // callback asked to destroy the track while we were still using it.
MIX_DestroyTrack(track); // actually kill it now.
}
}