Skip to content

Commit 06ba0c0

Browse files
committed
oto: run Suspend/Resume off the calling thread on darwin
Suspend and Resume held c.cond.L to flip the toPause/toResume flags. On iOS the audio render goroutine can hold c.cond.L for several seconds while resume() retries transient AVAudioSession errors (e.g. AVAudioSessionErrorCodeCannotStartPlaying), which meant the platform UI thread could block in applicationWillResignActive / applicationDidBecome Active for the duration of those retries. Spawn a small goroutine to perform the cond.L acquisition and flag flip so the caller returns immediately. The underlying audio loop, retry policy, and error semantics are unchanged; errors from the asynchronous transition still surface via Err on the next poll. Updates #93
1 parent a70e54a commit 06ba0c0

1 file changed

Lines changed: 26 additions & 20 deletions

File tree

driver_darwin.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -254,30 +254,36 @@ func (c *context) appendBuffer(buf32 []float32) {
254254
}
255255
}
256256

257+
// Suspend returns immediately. The actual AudioQueuePause runs on a
258+
// background goroutine so the calling thread (typically the platform UI
259+
// thread) is never blocked on cond.L — which can be held for several seconds
260+
// while resume() retries transient AVAudioSession errors. Errors from the
261+
// asynchronous transition surface via Err.
257262
func (c *context) Suspend() error {
258-
c.cond.L.Lock()
259-
defer c.cond.L.Unlock()
260-
261-
if err := c.err.Load(); err != nil {
262-
return err.(error)
263-
}
264-
c.toPause = true
265-
c.toResume = false
266-
c.cond.Signal()
267-
return nil
263+
err := c.err.Load()
264+
go func() {
265+
c.cond.L.Lock()
266+
defer c.cond.L.Unlock()
267+
c.toPause = true
268+
c.toResume = false
269+
c.cond.Signal()
270+
}()
271+
return err
268272
}
269273

274+
// Resume returns immediately. See Suspend for the rationale; AudioQueueStart
275+
// (with retries on transient AVAudioSession errors on iOS) runs on a
276+
// background goroutine.
270277
func (c *context) Resume() error {
271-
c.cond.L.Lock()
272-
defer c.cond.L.Unlock()
273-
274-
if err := c.err.Load(); err != nil {
275-
return err.(error)
276-
}
277-
c.toPause = false
278-
c.toResume = true
279-
c.cond.Signal()
280-
return nil
278+
err := c.err.Load()
279+
go func() {
280+
c.cond.L.Lock()
281+
defer c.cond.L.Unlock()
282+
c.toPause = false
283+
c.toResume = true
284+
c.cond.Signal()
285+
}()
286+
return err
281287
}
282288

283289
func (c *context) pause() error {

0 commit comments

Comments
 (0)