internal/hook: SuspendAudio/ResumeAudio flips the flag before running hooks, diverging permanently on hook error
Ebitengine Version
main @ 4f474b00d (after v2.9.11)
Operating System
- Windows
- macOS
- Linux
- FreeBSD
- OpenBSD
- Android
- iOS
- Nintendo Switch
- PlayStation 5
- Xbox
- Web Browsers
Go Version (paste your go version output)
go version go1.26.7 linux/amd64
What steps will reproduce the problem?
- Register audio suspend/resume hooks with an implementation that can fail (e.g. an audio-device operation failing during backgrounding on mobile):
hook.OnSuspendAudio(func() error {
return errors.New("device busy") // e.g. transient OS failure
})- Trigger
hook.SuspendAudio(). It returns the error — but the damage is done. Trigger it again (or check the state): the second call is a silent no-op even though the device was never actually suspended.
Root cause (internal/hook/hook.go:86-110 on current main):
func SuspendAudio() error {
...
audioSuspended = true // committed BEFORE the hook runs (line 92)
if onSuspendAudio != nil {
return onSuspendAudio() // error leaves the flag flipped (line 94)
}
return nil
}ResumeAudio mirrors this (flag cleared at line 105 before onResumeAudio() at line 107). When a hook fails, the flag permanently disagrees with reality: every later SuspendAudio no-ops ("already suspended") and every later ResumeAudio no-ops ("not suspended"). The audio state machine is wedged until process restart — typically permanent wrong behavior (stuck muted, or suspend never re-applied).
What is the expected result?
The hooks should run first, and the audioSuspended flag should only be committed on success, so a transient hook failure leaves the state machine consistent and retryable.
What happens instead?
One transient failure (exactly the kind that happens when an app is backgrounded/foregrounded on mobile) permanently desynchronizes the flag from the device. From a player's perspective: after returning to the game once, audio is stuck off (or misbehaves) forever, with no recovery short of restarting the app.
Anything else you feel useful to add?
- Severity: high for game users, especially on mobile where suspend/resume around backgrounding is a core OS interaction, not an edge case.
- Suggested fix: reorder to invoke
onSuspendAudio/onResumeAudiofirst and setaudioSuspendedonly when the hook returns nil.
Source: hajimehoshi/ebiten