Fix nasty race condition with menu background audio. Fixes #12365

This commit is contained in:
Henrik Rydgård 2019-09-27 23:53:31 +02:00
parent 701d740207
commit 22ee875ffc
4 changed files with 41 additions and 27 deletions

View File

@ -223,7 +223,7 @@ private:
SimpleAudio *decoder_ = nullptr;
};
static std::mutex bgMutex;
static std::mutex g_bgMutex;
static std::string bgGamePath;
static int playbackOffset;
static AT3PlusReader *at3Reader;
@ -251,7 +251,7 @@ static void ClearBackgroundAudio(bool hard) {
void SetBackgroundAudioGame(const std::string &path) {
time_update();
std::lock_guard<std::mutex> lock(bgMutex);
std::lock_guard<std::mutex> lock(g_bgMutex);
if (path == bgGamePath) {
// Do nothing
return;
@ -272,7 +272,7 @@ void SetBackgroundAudioGame(const std::string &path) {
int PlayBackgroundAudio() {
time_update();
std::lock_guard<std::mutex> lock(bgMutex);
std::lock_guard<std::mutex> lock(g_bgMutex);
// Immediately stop the sound if it is turned off while playing.
if (!g_Config.bEnableSound) {
@ -281,30 +281,6 @@ int PlayBackgroundAudio() {
return 0;
}
// If there's a game, and some time has passed since the selected game
// last changed... (to prevent crazy amount of reads when skipping through a list)
if (!at3Reader && bgGamePath.size() && (time_now_d() - gameLastChanged > 0.5)) {
// Grab some audio from the current game and play it.
if (!g_gameInfoCache)
return 0; // race condition?
// This is very unsafe! TODO: Get rid of this somehow or make threadsafe!
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(NULL, bgGamePath, GAMEINFO_WANTSND);
if (!gameInfo)
return 0;
if (gameInfo->pending) {
// Should try again shortly..
return 0;
}
if (gameInfo->sndFileData.size()) {
const std::string &data = gameInfo->sndFileData;
at3Reader = new AT3PlusReader(data);
lastPlaybackTime = 0.0;
}
}
double now = time_now();
if (at3Reader) {
int sz = lastPlaybackTime <= 0.0 ? 44100 / 60 : (int)((now - lastPlaybackTime) * 44100);
@ -336,3 +312,33 @@ int PlayBackgroundAudio() {
return 0;
}
// Stuff that should be on the UI thread only, like anything to do with
// g_gameInfoCache.
void UpdateBackgroundAudio() {
// If there's a game, and some time has passed since the selected game
// last changed... (to prevent crazy amount of reads when skipping through a list)
if (bgGamePath.size() && (time_now_d() - gameLastChanged > 0.5)) {
std::lock_guard<std::mutex> lock(g_bgMutex);
if (!at3Reader) {
// Grab some audio from the current game and play it.
if (!g_gameInfoCache)
return;
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(NULL, bgGamePath, GAMEINFO_WANTSND);
if (!gameInfo)
return;
if (gameInfo->pending) {
// Should try again shortly..
return;
}
if (gameInfo->sndFileData.size()) {
const std::string &data = gameInfo->sndFileData;
at3Reader = new AT3PlusReader(data);
lastPlaybackTime = 0.0;
}
}
}
}

View File

@ -4,3 +4,4 @@
void SetBackgroundAudioGame(const std::string &path);
int PlayBackgroundAudio();
void UpdateBackgroundAudio();

View File

@ -734,6 +734,7 @@ void GameInfoCache::WaitUntilDone(std::shared_ptr<GameInfo> &info) {
// Runs on the main thread. Only call from render() and similar, not update()!
// Can also be called from the audio thread for menu background music.
std::shared_ptr<GameInfo> GameInfoCache::GetInfo(Draw::DrawContext *draw, const std::string &gamePath, int wantFlags) {
std::shared_ptr<GameInfo> info;

View File

@ -959,6 +959,12 @@ void RenderOverlays(UIContext *dc, void *userdata) {
void NativeRender(GraphicsContext *graphicsContext) {
g_GameManager.Update();
if (GetUIState() != UISTATE_INGAME) {
// Note: We do this from NativeRender so that the graphics context is
// guaranteed valid, to be safe - g_gameInfoCache messes around with textures.
UpdateBackgroundAudio();
}
float xres = dp_xres;
float yres = dp_yres;