fix(host/windows): DuplicateOutput1 retry wait 200ms (Apollo's value), env-tunable
The old-dup kernel teardown takes ~200ms (Apollo waits exactly that), so the previous 2-16ms retries were too short and still fell through to the churning legacy dup. Bump to PUNKTFUNK_DUP_RETRY_MS (default 200) x PUNKTFUNK_DUP_RETRY_N (default 6) so the robust DuplicateOutput1 dup wins the race. Env-tunable for on-box dialing without a rebuild. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -194,21 +194,32 @@ unsafe fn duplicate_output(
|
|||||||
// which "succeeds" into a fragile dup that churns ACCESS_LOST/MODE_CHANGE every few ms on this
|
// which "succeeds" into a fragile dup that churns ACCESS_LOST/MODE_CHANGE every few ms on this
|
||||||
// cross-GPU IDD. (This is why DuplicateOutput1 failed but the legacy call a beat later
|
// cross-GPU IDD. (This is why DuplicateOutput1 failed but the legacy call a beat later
|
||||||
// succeeded — pure timing. Apollo retries DuplicateOutput1 2x/200ms for the same reason.)
|
// succeeded — pure timing. Apollo retries DuplicateOutput1 2x/200ms for the same reason.)
|
||||||
|
// Apollo waits 200 ms between DuplicateOutput1 attempts — the kernel-side teardown of the
|
||||||
|
// just-released duplication takes that long, so short (ms) waits aren't enough. Env-tunable so
|
||||||
|
// we can dial it without a rebuild: PUNKTFUNK_DUP_RETRY_MS (per-wait, default 200) ×
|
||||||
|
// PUNKTFUNK_DUP_RETRY_N (attempts, default 6) → ~1 s worst case before the legacy fallback.
|
||||||
|
let retry_ms: u64 = std::env::var("PUNKTFUNK_DUP_RETRY_MS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(200);
|
||||||
|
let attempts: u64 = std::env::var("PUNKTFUNK_DUP_RETRY_N")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(6)
|
||||||
|
.max(1);
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
for attempt in 0..5u64 {
|
for attempt in 0..attempts {
|
||||||
match output5.DuplicateOutput1(device, 0, &formats) {
|
match output5.DuplicateOutput1(device, 0, &formats) {
|
||||||
Ok(d) => {
|
Ok(d) => {
|
||||||
if attempt > 0 {
|
if attempt > 0 {
|
||||||
tracing::info!(attempt, "DuplicateOutput1 succeeded on retry (raced old-dup teardown)");
|
tracing::info!(attempt, "DuplicateOutput1 succeeded on retry (rode out old-dup teardown race)");
|
||||||
}
|
}
|
||||||
return Ok(d);
|
return Ok(d);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
last_err = Some(e);
|
last_err = Some(e);
|
||||||
// Escalating brief waits: 2,4,8,16 ms (skip after the last attempt). Bounded so a
|
if attempt + 1 < attempts {
|
||||||
// GENUINE failure still falls back to legacy quickly (~30 ms worst case).
|
std::thread::sleep(Duration::from_millis(retry_ms));
|
||||||
if attempt < 4 {
|
|
||||||
std::thread::sleep(Duration::from_millis(2u64 << attempt));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user