Skip to content

fix: guard playTo() against reset() nulling the iterator mid-await - #46

Open
PriyeshPandey2000 wants to merge 2 commits into
diffusionstudio:mainfrom
PriyeshPandey2000:fix/audio-playto-reset-race
Open

fix: guard playTo() against reset() nulling the iterator mid-await#46
PriyeshPandey2000 wants to merge 2 commits into
diffusionstudio:mainfrom
PriyeshPandey2000:fix/audio-playto-reset-race

Conversation

@PriyeshPandey2000

Copy link
Copy Markdown

Summary

  • AudioDecoder.playTo() holds an AsyncMutex, but reset() (called synchronously by the playback system on play→pause) nulls this.iterator without acquiring it. If reset() lands mid-await inside playTo's decode loop, the resumed iteration reads a nulled iterator and throws TypeError: Cannot read properties of null (reading 'next').
  • Fix: snapshot this.iterator per loop iteration and re-check its identity after each await before trusting it, instead of reading the mutable field directly across suspension points.
  • Added a vitest regression test reproducing the race (fails with the reported TypeError pre-fix, passes post-fix). This is the first test in the repo -> added vitest as a dev dependency to packages/runtime only; happy to drop the test/dependency if you'd rather not introduce a test runner yet, the code fix stands on its own either way.

Repro

Play a clip, pause mid-decode. The realtime engine drives playbackSystem synchronously off requestAnimationFrame without awaiting playTo's promise, so reset() on the next tick can land while a previous tick's playTo is still suspended on an in-flight decode , timing-dependent, but the window exists on every frame where playback is paused mid-decode.

Test plan

  • cd packages/runtime && npx vitest run — 2/2 pass

reset() (called synchronously on play->pause) nulls this.iterator
without acquiring playTo's mutex, so it can race an in-flight decode
and throw "Cannot read properties of null (reading 'next')". Snapshot
the iterator per loop iteration and re-check identity after each
await instead of trusting the mutable field directly.

Add a vitest regression test reproducing the race.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

@PriyeshPandey2000 is attempting to deploy a commit to the Diffusion Studio Team on Vercel.

A member of the Team first needs to authorize it.

The iterator identity checks miss two windows: a playTo queued behind
the mutex, and one suspended on the reseed branch's iterator.return().
In both the iterator is null before and after the reset, so identity
cannot detect it, and playTo goes on to reseed a decode and schedule
audio for a clip the user already paused.

Track a generation counter bumped on every reset() and bail whenever
it moves while playTo is suspended.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant