What is the case
DiscardPendingPdus(long arrivedBefore) runs on the actor in ClearReceptionsBefore: it takes every item out of the PDU inbox, then writes back the ones that arrived at or after the stamp. Dispose completes the inbox (_pduInbox.Writer.TryComplete()) from the caller's thread, unsynchronised with that. If Dispose runs between the drain and the write-back, every TryWrite returns false and the PDUs the discard promised to keep are lost.
On main the two statements are IsoTpChannel.cs (foreach (var item in kept) _pduInbox.Writer.TryWrite(item); in ClearReceptionsBefore) and _pduInbox.Writer.TryComplete(); in Dispose; this predates #261 (found there by Codex, round 6; the branch did not introduce it).
Why it is small
After Dispose the channel is going away; a receiver can still read what is left and then gets the "channel is disposed" error, so the loss only matters to a receiver that is still reading while the channel is disposed under it and a discard is in flight at the same moment.
Options
- Complete the inbox on the actor (in the cleanup
Dispose already posts), keeping the direct completion as the fallback when the actor is gone. Serialises the two; changes when a waiting receiver is woken by Dispose from "immediately" to "when the actor runs the cleanup".
- Leave it and document it: items retained by a discard that overlaps
Dispose are not guaranteed.
Needs a decision on which; the first one touches the dispose ordering the class comment explains.
Found while reviewing #261.
🤖 Generated with Claude Code
What is the case
DiscardPendingPdus(long arrivedBefore)runs on the actor inClearReceptionsBefore: it takes every item out of the PDU inbox, then writes back the ones that arrived at or after the stamp.Disposecompletes the inbox (_pduInbox.Writer.TryComplete()) from the caller's thread, unsynchronised with that. IfDisposeruns between the drain and the write-back, everyTryWritereturnsfalseand the PDUs the discard promised to keep are lost.On
mainthe two statements areIsoTpChannel.cs(foreach (var item in kept) _pduInbox.Writer.TryWrite(item);inClearReceptionsBefore) and_pduInbox.Writer.TryComplete();inDispose; this predates #261 (found there by Codex, round 6; the branch did not introduce it).Why it is small
After
Disposethe channel is going away; a receiver can still read what is left and then gets the "channel is disposed" error, so the loss only matters to a receiver that is still reading while the channel is disposed under it and a discard is in flight at the same moment.Options
Disposealready posts), keeping the direct completion as the fallback when the actor is gone. Serialises the two; changes when a waiting receiver is woken byDisposefrom "immediately" to "when the actor runs the cleanup".Disposeare not guaranteed.Needs a decision on which; the first one touches the dispose ordering the class comment explains.
Found while reviewing #261.
🤖 Generated with Claude Code