Skip to content

Harden the PlatformAudio lifecycle (PAR-025) - #377

Draft
MaxHeimbrock wants to merge 1 commit into
max/par-024-android-lazy-session-acquisitionfrom
max/par-025-platformaudio-lifecycle-hardening
Draft

Harden the PlatformAudio lifecycle (PAR-025)#377
MaxHeimbrock wants to merge 1 commit into
max/par-024-android-lazy-session-acquisitionfrom
max/par-025-platformaudio-lifecycle-hardening

Conversation

@MaxHeimbrock

Copy link
Copy Markdown
Contributor

Background

PR #374's review left three low-severity sharp edges on the phase-A PlatformAudio code (finding cluster N, tracked as PAR-025). None is user-visible in the shipped samples, but all three bite apps built on the API: methods called after Dispose() reach a disposed FfiHandle (or, on iOS, drive the audio session after it was handed back to Unity); a route-controller failure in the constructor leaks the FFI handle until the SafeHandle finalizer runs; and SelectOutput's deferral while session audio is disabled gave a pre-call device picker no stated way to track when its choice actually applies.

Stacked on #376 (PAR-024), which is stacked on #374 (PAR-023).

Changes

  • Every public PlatformAudio member now throws ObjectDisposedException after Dispose(). Subscribing to / unsubscribing from DevicesChanged and double-Dispose stay safe (the existing PlayMode test still passes); _disposed is set at the top of Dispose so the guard takes effect immediately.
  • The constructor wraps its post-handle work: when route-controller creation fails, the FFI handle (and a half-built controller) are disposed before the exception propagates.
  • SelectOutput deferral signal: resolved doc-only, no API addition — the PAR-019 surface stays frozen. The app already holds both inputs of "is my choice pending" (its own SelectOutput call and its own session-enable state); application ground-truth arrives via the existing IsSelected flip in GetDevices/DevicesChanged, and the silent drop case (a deferred choice whose device disappears) is visible as the device leaving the playout list. A pending signal would carry meaning only on Android (desktop applies immediately, iOS throws), which is the platform-conditional shape PAR-019 deliberately avoided; phase B's FFI device model (PAR-003) is the right layer if a cross-SDK signal proves needed. The SelectOutput XML doc and the README routing section now spell out the tracking recipe.
  • New PlayMode test PublicMembers_AfterDispose_ThrowObjectDisposed; full PlatformAudioTests suite passes on 2022.3.62f1 (10 passed, 1 pre-existing environment skip). The ctor-leak fix is not editor-testable: the desktop route controller's construction cannot be made to fail.

No device test required for this card (human_required: none).

🤖 Generated with Claude Code

Every public member now throws ObjectDisposedException after Dispose()
instead of reaching a disposed FfiHandle (or, on iOS, driving the audio
session after it was handed back). DevicesChanged subscribe/unsubscribe
and double-Dispose stay safe, as before.

The constructor no longer leaks the FFI handle when route-controller
creation fails after the handle exists: the post-handle work is wrapped
so the handle (and a half-built controller) are disposed before the
exception propagates, instead of waiting on the SafeHandle finalizer.

The SelectOutput deferral while session audio is disabled deliberately
gets no pending flag (PAR-019 keeps the surface frozen); the XML doc and
the README routing section now spell out how a pre-call picker tracks
its deferred choice through the existing IsSelected/DevicesChanged
surface, including the drop-on-disappear case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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