Skip to content

Fix AudioPlayer disposal reentrancy - #218

Merged
jfversluis merged 2 commits into
mainfrom
jfversluis-fix-player-disposal-reentrancy
Aug 13, 2026
Merged

jfversluis merged 2 commits into
mainfrom
jfversluis-fix-player-disposal-reentrancy

Conversation

@jfversluis

@jfversluis jfversluis commented Aug 5, 2026 •

Copy link
Copy Markdown
Owner

Description

Hardens the Windows AudioPlayer completion/disposal path so disposing from PlaybackEnded cannot recurse or deliver a queued completion after disposal.

  • Keeps explicit Stop() behavior unchanged: it still raises PlaybackEnded.
  • Uses a non-notifying internal stop during disposal.
  • Marks the player disposed before teardown and ignores completion callbacks after disposal.
  • Incorporates the merged Fix iOS crash when disposing a completed audio player #219 Apple disposal fix without changing its deferred callback behavior.

Fixes #214.

A dedicated regression test is not included because the repository has no test project and this behavior depends on native MediaPlayer callbacks. The Windows plugin/sample CI builds and generated NuGet artifact provide the practical validation path.

Platforms Affected

  • Android
  • iOS
  • macOS (Catalyst)
  • Windows

Breaking Changes

None.

Checklist

  • Plugin builds successfully
  • Sample builds (CI)
  • New public APIs have XML docs (N/A; no public API changes)
  • README updated (N/A; no API changes)

Prevent disposal and source replacement from raising PlaybackEnded while native resources are being torn down. Mark players disposed before cleanup and ignore queued native completion callbacks on Windows and Apple platforms.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 56d5ce10-8e4c-4df1-9c08-b4596ebb6c10
Copilot AI lite review requested due to automatic review settings August 5, 2026 17:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a Windows stack overflow scenario caused by AudioPlayer.Dispose() re-entering PlaybackEnded during teardown, by preventing disposal and source-replacement cleanup from emitting PlaybackEnded while native resources are being released. The change aligns Windows and iOS/macOS behavior by separating “explicit Stop notification” from “silent cleanup” paths and by ignoring completion callbacks after disposal begins.

Changes:

  • Windows: suppresses PlaybackEnded reentrancy by short-circuiting completion callbacks once disposal starts and by using a non-notifying stop during Dispose.
  • iOS/macOS: mirrors the same pattern by adding a non-notifying stop path for disposal/source replacement and ignoring finished-playing callbacks once disposed.
  • Preserves public Stop() behavior by routing it through a notifying stop path, while internal cleanup uses a non-notifying variant.
Show a summary per file
File Description
src/Plugin.Maui.Audio/AudioPlayer/AudioPlayer.windows.cs Adds isDisposed guard to completion callback and introduces Stop(bool raisePlaybackEnded) to avoid raising PlaybackEnded during disposal.
src/Plugin.Maui.Audio/AudioPlayer/AudioPlayer.macios.cs Introduces non-notifying stop for disposal/source replacement, marks disposed earlier, and ignores FinishedPlaying callbacks once disposed.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite

Resolve the overlap with #219 by retaining its deferred Apple completion handling while preserving the Windows reentrancy guard and non-notifying disposal path for #214.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 56d5ce10-8e4c-4df1-9c08-b4596ebb6c10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (1)

src/Plugin.Maui.Audio/AudioPlayer/AudioPlayer.windows.cs:159

  • PlaybackEnded is raised with the native MediaPlayer as the event sender on Windows (sender), but other platform implementations raise it with the AudioPlayer instance (this). This makes the event sender inconsistent across platforms and can break consumers that treat sender as IAudioPlayer. Consider raising with this here and ignoring the native sender parameter.

		PlaybackEnded?.Invoke(sender, EventArgs.Empty);
	}
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@jfversluis
jfversluis merged commit d2b50e4 into main Aug 13, 2026
3 checks passed
@jfversluis
jfversluis deleted the jfversluis-fix-player-disposal-reentrancy branch August 13, 2026 13:05
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.

System.StackOverflowException when disposing AudioPlayer in PlaybackEnded event on Windows

2 participants