Skip to content

A Drive that fails and rejoins inside one audio block keeps its new join request (#945) - #956

Merged
Skeyelab merged 1 commit into
mainfrom
agent3/rejoin-race
Oct 25, 2026
Merged

Skeyelab merged 1 commit into
mainfrom
agent3/rejoin-race

Conversation

@Skeyelab

Copy link
Copy Markdown
Member

Closes #945.

Real-time path (ADR 0002): human review of the diff. This changes the join block in Capture.receive, which runs on the audio thread. Atomics only: no locks, no allocation, no blocking calls (checked by a Code Reviewer).

The bug

The join block read joinRequests, acted on it, then cleared what it read. A stopFeeding plus attachMidTake of the same Copy landing in between had the new request wiped, and the Copy was fed with no join frame.

The fix

  • The join block claims the requests with one exchange(0) instead of a load and a later bitwiseAnd. A request posted after the claim survives to the next block. A block with nothing waiting still does only a plain read.
  • The A Drive that fails as it joins mid-Take can report dropped frames that never happened #544 stale check now takes out of capturing any claimed Copy that is stopped or has a fresh request pending. That Copy isn't fed on this block; the next block serves the fresh request at the right join frame.
  • The test-only hook afterJoinRead (nil in the app) moved to after the join frames are set, before the Copies are added to capturing, so the race is reachable.

Tests

  • stopAndRejoinDuringJoinBlock: the Copy is stopped and attached again from inside the hook. Red first: before the fix, joinedAt was nil and the ring held 960 frames. Now it isn't fed on that block, then joins at 960 with 480 frames.
  • The 13 existing Copy join tests, the A Drive that fails as it joins mid-Take can report dropped frames that never happened #544 ones included, pass with the hook moved.
  • The reviewer's mutation run: removing the exchange or the fresh-request take-out turns the new test red.
  • VERIFY_QUICK=1 ./scripts/verify.sh passed (no App/ change).

Still open, not in this PR

All very narrow. TakeSession starts a rejoin only after an async Drive prepare, so it isn't expected to produce these:

  • Partial overlaps of the same race: a reattach whose reset lands between the claim and the join frames being set, or between its own stopped-clear and request-set, can still leave a Copy one block (about 10 ms) out of line. Closing these needs the stopped and request bits updated in one step, or a generation per request.
  • Stop during a join block (older than this change): a stopCapturing landing between the claim and the OR into capturing puts the claimed Copies back after Stop. One option: stopCapturing sets every stoppedFeeds bit.
  • No test pins the take-out of a Copy stopped during a join block, because the per-block mask hides it. Pinning it would need a second hook.

Device Take needed (before External testers)

Checked by tests only. Ship in the next [internal] tag. Eric's Take from that TestFlight build: a Drive that fails as it joins mid-Take and is joined again, with the Drive's Stems in line with the Device's and no false dropped frames in the report.

…oin request (#945)

The join block read the requests, acted on them, then cleared what it
read. A stop and a reattach landing in between had their new request
wiped, and the Copy was fed with no join frame. The block now claims
the requests with one exchange, so a later request survives to the
next block, and a Copy stopped or asked to join again since the claim
is taken back out of capturing until that block serves it.
Real-time path (ADR 0002): atomics only.

Assisted-by: Claude Code:claude-opus-5-5
@Skeyelab
Skeyelab enabled auto-merge (squash) October 25, 2026 03:01
@codecov

codecov Bot commented Oct 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Skeyelab
Skeyelab merged commit 91faf08 into main Oct 25, 2026
11 checks passed
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.

A Drive that fails and rejoins inside one audio block can lose its new join request

1 participant