fix: assign seek CTS synchronously and guard load finally to stop seek/load race
This commit is contained in:
@@ -184,7 +184,12 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
IsLoading = false;
|
IsLoading = false;
|
||||||
await NotifyStateChanged();
|
// Only notify if this load is still the active operation. A superseding seek
|
||||||
|
// owns state notifications; firing here mid-seek would push a stale snapshot.
|
||||||
|
if (ReferenceEquals(_streamingCancellation, loadCts))
|
||||||
|
{
|
||||||
|
await NotifyStateChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,11 +447,21 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
|
|||||||
// OperationCanceledException asynchronously; if we kick off a new loop
|
// OperationCanceledException asynchronously; if we kick off a new loop
|
||||||
// immediately, both can race against the single-instance JS StreamDecoder
|
// immediately, both can race against the single-instance JS StreamDecoder
|
||||||
// and corrupt decode state. Draining here is the load-bearing guarantee.
|
// and corrupt decode state. Draining here is the load-bearing guarantee.
|
||||||
_streamingCancellation?.Cancel();
|
//
|
||||||
await DrainActiveStreamingTaskAsync();
|
// Invariant: any caller that supersedes a load WITHOUT wanting the load's
|
||||||
_streamingCancellation?.Dispose();
|
// state reset must assign its own CTS to _streamingCancellation *before*
|
||||||
|
// its first await. LoadTrackStreaming's OCE continuation fires during the
|
||||||
|
// drain await on the shared _activeStreamingTask; it resets IsLoaded/
|
||||||
|
// IsStreamingMode only when _streamingCancellation still equals its loadCts.
|
||||||
|
// Assigning seekCts synchronously here makes that identity check fail, so
|
||||||
|
// the seek's state survives. (ResetToIdle deliberately does NOT do this —
|
||||||
|
// it wants the reset, and nulls _streamingCancellation only after the drain.)
|
||||||
|
var oldCts = _streamingCancellation;
|
||||||
var seekCts = new CancellationTokenSource();
|
var seekCts = new CancellationTokenSource();
|
||||||
_streamingCancellation = seekCts;
|
_streamingCancellation = seekCts;
|
||||||
|
oldCts?.Cancel();
|
||||||
|
await DrainActiveStreamingTaskAsync();
|
||||||
|
oldCts?.Dispose();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user