Commit e151dd8
committed
fix(15.1-07a): pick PeerNextDsUpdate slot in apply visitor (random remote cutout)
User UAT on build 289: remote audio cuts out RANDOMLY during intervals
(different from the every-other-interval cutout fixed in build 289).
Root cause of the new symptom: build-289's mitigation (always publish
to slot 0) defer-deleted any pending next_ds[0] on every publish. That
silently dropped data when the run thread published 2+ updates between
two audio drains — which happens under bursty network arrivals. Result:
random missed intervals → random audible cutouts.
The deeper architectural mistake (in 07a originally, not just my mitigation):
slot selection was placed on the run-thread publisher. The 07a executor
used a stateful XOR shadow because "the run thread can't read mirror
state" — but this overlooked that the apply visitor (which IS where the
slot is actually written into the mirror) RUNS ON THE AUDIO THREAD via
drainRemoteUserUpdates() called from AudioProc. The audio thread CAN
read its own state. Slot picking should always have been there.
Fix: move slot selection into the apply visitor at line 3290+. Ignore
the publisher's u.slot_idx field. Apply visitor logic:
- if next_ds[0] is empty → fill slot 0
- else if next_ds[1] is empty → queue at slot 1
- else (both full, extremely rare) → defer-delete oldest (next_ds[0]),
shift, queue new ds at slot 1
This recreates the legacy `useidx = !!next_ds[0]` semantic on the
correct (audio) thread, no concurrency required (both writers to
next_ds[2] — this apply visitor AND the on_new_interval shuffle —
already run on the audio thread).
Sequence under bursty arrivals (the failure mode of build 289):
T0: run publishes A; run publishes B (both before audio drain)
T0 audio drain: apply A → next_ds[0]=A; apply B → next_ds[0] occupied
→ next_ds[1]=B (was: defer-delete A, lose interval)
T0 interval boundary: ds=A; shift → next_ds[0]=B; next_ds[1]=null
T1 audio drain: (no updates)
T1 interval boundary: ds=B; shift → next_ds[0]=null
Both A and B audible. No cutout.
Sequence under normal flow (1 publish per interval):
T0: run publishes A
T0 audio drain: next_ds[0]=A
T0 interval: ds=A; shift → next_ds[0]=null
T1: run publishes B
T1 audio drain: next_ds[0]=B
T1 interval: ds=B
Continuous.
The publisher u.slot_idx field stays in the payload (spsc_payloads.h
is FINAL per Codex M-9 — DO NOT modify) but is now ignored by the
apply visitor. The run-thread `run_thread_next_ds_idx` field at
njclient.cpp:544 is dead code; can be removed in 15.1-10 cleanup.
Build 290. Verifies on next UAT — remote audio should be continuous
under both steady-rate and bursty network arrivals. The random cutouts
should be gone.1 parent e827453 commit e151dd8
2 files changed
Lines changed: 35 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3288 | 3288 | | |
3289 | 3289 | | |
3290 | 3290 | | |
3291 | | - | |
3292 | | - | |
3293 | | - | |
3294 | | - | |
| 3291 | + | |
| 3292 | + | |
| 3293 | + | |
| 3294 | + | |
| 3295 | + | |
| 3296 | + | |
| 3297 | + | |
| 3298 | + | |
| 3299 | + | |
| 3300 | + | |
| 3301 | + | |
| 3302 | + | |
| 3303 | + | |
| 3304 | + | |
| 3305 | + | |
| 3306 | + | |
| 3307 | + | |
| 3308 | + | |
| 3309 | + | |
| 3310 | + | |
| 3311 | + | |
3295 | 3312 | | |
3296 | | - | |
3297 | | - | |
3298 | | - | |
3299 | | - | |
3300 | | - | |
| 3313 | + | |
| 3314 | + | |
| 3315 | + | |
| 3316 | + | |
| 3317 | + | |
| 3318 | + | |
| 3319 | + | |
| 3320 | + | |
| 3321 | + | |
| 3322 | + | |
| 3323 | + | |
| 3324 | + | |
| 3325 | + | |
3301 | 3326 | | |
3302 | 3327 | | |
3303 | 3328 | | |
| |||
0 commit comments