preserve unread data across detach#831
Conversation
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
| var advanced = false; | ||
|
|
||
| if (cancellationToken.IsCancellationRequested) | ||
| { |
There was a problem hiding this comment.
need comment to explain why it is required
There was a problem hiding this comment.
Agreed. I will add a comment here to explain why this is required: when cancellation is observed after ReadAsync has already returned buffered data, we intentionally avoid advancing past any unread bytes so the remaining buffer can still be preserved for the detach/next owner path instead of being dropped.
| { | ||
| if (bufferFilterResult.Consumed > 0) |
There was a problem hiding this comment.
need comment to explain the if/else here
There was a problem hiding this comment.
Agreed. I will add a comment for this branch. The Consumed > 0 case means the parser made real progress for a package, so the reader can advance to that consumed position. The else case handles a package result without consumed bytes, where we should only mark the current buffer as examined and avoid dropping unread data.
| advanced = true; | ||
| yield return bufferFilterResult.Package; | ||
|
|
||
| if (cancellationToken.IsCancellationRequested) |
There was a problem hiding this comment.
need comment to explain the if/else here
There was a problem hiding this comment.
Agreed. I will add a comment here as well. After yielding a package, cancellation may be requested by the consumer, so this check stops further parsing while keeping the already-advanced position consistent with the package that was just delivered.
|
|
||
| namespace KestrelHttp2DetachDemo; | ||
|
|
||
| internal sealed class Http2PipelineFilter : PipelineFilterBase<Http2Frame> |
There was a problem hiding this comment.
should it be in SuperSocket.Http?
There was a problem hiding this comment.
Good point. I kept it in the sample because it is currently only a minimal HTTP/2 frame parser for demonstrating the Kestrel detach handoff scenario, not a reusable/general HTTP implementation yet. If you prefer, I can move it under SuperSocket.Http and wire the sample to consume it from there.
|
I also reran the same commit ( Fork Actions run: https://github.com/interface95/SuperSocket/actions/runs/26434481311 Result: Ubuntu, macOS, and Windows all passed. The upstream macOS failure appears to be flaky ( |
…es on cancel Address review feedback on PR kerryjiang#831: explain the cancel-before-parse exit, the Consumed>0/==0 split when yielding a package, and the cancel-after-yield re-check that drives loop exit while keeping AdvanceTo consistent.
|
Thanks for the review. Pushed 0c368a9:
|
|
The unit test failed: |
| @@ -431,12 +469,12 @@ protected async IAsyncEnumerable<TPackageInfo> ReadPipeAsync<TPackageInfo>(PipeR | |||
|
|
|||
| pipelineFilter = _pipelineFilter as IPipelineFilter<TPackageInfo>; | |||
|
|
|||
There was a problem hiding this comment.
You can put this piece of logic in a bigger if scope
if (!advanced)
{
xxx;
}
There was a problem hiding this comment.
Done — consolidated with the change above. The old if (!advanced) block is now absorbed into the single AdvanceTo call site: when advanced == false, examined is set to buffer.End, which is the same behavior as before. No separate scope needed anymore.
See commit ce9c073.
| // Consumed == 0: the filter produced a package without reporting | ||
| // consumption (e.g. it kept its own state); mark the whole buffer | ||
| // as examined but only Start as consumed so no bytes are dropped. | ||
| if (bufferFilterResult.Consumed > 0) |
There was a problem hiding this comment.
If you want o advance for each package, you can move this piece of code out from the if statement and then you can remove the advance logic for lastFilterResult which is outside of the foreach.
There was a problem hiding this comment.
Done. Moved the AdvanceTo out of the per-package if block and merged it with the post-loop advance into a single call site. The advanced flag now selects the examined position:
- advanced == true: examined = consumed (preserve unread bytes for next iteration / detach handoff)
- advanced == false: examined = buffer.End (mark entire buffer as examined)
See commit ce9c073.
|
@interface95 left more comments |
|
The only failure is |
…ance point Move reader.AdvanceTo() out of the per-package if block and merge it with the post-loop advance, using the 'advanced' flag to select the examined position: advanced == true → examined = consumed (preserve unread bytes) advanced == false → examined = buffer.End (mark all as examined) This addresses the review feedback on PR kerryjiang#831 (comments 3322484623, 3322528441) without changing runtime behavior.
Summary
Verification