Skip to content

Use ArrayPool as default pool fallback #35719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 7, 2021
Merged

Use ArrayPool as default pool fallback #35719

merged 8 commits into from
Sep 7, 2021

Conversation

sebastienros
Copy link
Member

@sebastienros sebastienros commented Aug 25, 2021

GetFakeMemory() can throw an exception if sizeHint is greater than MaxPoolSize. The default pool is PinnedBlockMemoryPool with a max size of 4096.

Fixes #30364

@sebastienros
Copy link
Member Author

Once approved I will start the process of backporting it to 3.1 and 5.0.

@halter73
Copy link
Member

Can we add some regression tests for this?

@sebastienros
Copy link
Member Author

Updated with feedback and unit tests.
I also used the repro code provided with the issue to confirm the bug, then tried these changes to confirm it was fixing it.

@davidfowl
Copy link
Member

Wouldn't it be simpler to support a null fake memory owner and just use ArrayPool.Shared?

@halter73
Copy link
Member

Wouldn't it be simpler to support a null fake memory owner and just use ArrayPool.Shared?

What's the benefit? Wouldn't you need to special case returning the array in Dispose() in that case?

@sebastienros
Copy link
Member Author

I would understand @davidfowl 's suggestion as replacing _fakeMemoryOwner by byte[] _fakeMemory which would always come from the ArrayPool.Shared if set, and then returned in Dispose. So as single source. But I don't know why it was using _memoryPool before vs. the array pool.

@davidfowl
Copy link
Member

What's the benefit? Wouldn't you need to special case returning the array in Dispose() in that case?

This code originally came from https://source.dot.net/#System.IO.Pipelines/System/IO/Pipelines/BufferSegment.cs,56 which handles this in a much nicer way IMO.

@halter73
Copy link
Member

What's nicer about BufferSegment's way of doing it?

@davidfowl
Copy link
Member

It's isolated in the buffer segment and doesn't allocate another kind of IMemoryOwner<byte>.

@sebastienros
Copy link
Member Author

I am trying to implement what I think you meant ... hope it's worth it. Will do it in a separate branch as I am very not sure about the end result.

@sebastienros
Copy link
Member Author

@davidfowl is this what you meant?

5de377a

@halter73
Copy link
Member

halter73 commented Sep 1, 2021

@davidfowl is this what you meant?

I'll try channeling @davidfowl. Allocating a FakeMemory isn't any better than allocating "another kind of IMemoryOwner<byte>.". This allocates even more than before in the case where the _memoryPool is used in FakeMemory.

If FakeMemory was pooled like BufferSegments are in System.IO.Pipelines, this wouldn't introduce a new allocation, but that's not the case here. We shouldn't bother pooling FakeMemory since this would only get allocated when a connection/request is aborted meaning the output producer itself will never be pooled at this point.

Instead the suggestion is to put separate IMemoryOwner<byte>? and byte[]? fields diretly in the output producers that are never both set at the same time similar to BufferSegment.


Now I'm going to speak as myself again. This seems like a lot of work to avoid a single extra allocation for aborted requests where the writer needs large buffers. This is a pretty rare scenario that isn't going to be very efficient to begin with. That said, it's not that much extra work I guess.

…r.cs

Co-authored-by: Stephen Halter <halter73@gmail.com>
…r.cs

Co-authored-by: Stephen Halter <halter73@gmail.com>
@sebastienros
Copy link
Member Author

@halter73 now I understand the comment, thanks.

@sebastienros
Copy link
Member Author

updated

@sebastienros
Copy link
Member Author

@davidfowl are the changes what you expected?

@sebastienros sebastienros enabled auto-merge (squash) September 7, 2021 15:03
@sebastienros sebastienros merged commit 764ac0a into main Sep 7, 2021
@sebastienros sebastienros deleted the sebros/fakememory branch September 7, 2021 15:11
@sebastienros
Copy link
Member Author

/backport to release/6.0

@github-actions
Copy link
Contributor

github-actions bot commented Sep 8, 2021

Started backporting to release/6.0: https://github.com/dotnet/aspnetcore/actions/runs/1211460440

@sebastienros
Copy link
Member Author

/backport to release/5.0

@github-actions
Copy link
Contributor

Started backporting to release/5.0: https://github.com/dotnet/aspnetcore/actions/runs/1238671987

@github-actions
Copy link
Contributor

@sebastienros backporting to release/5.0 failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch

Applying: Use ArrayPool as SlabMemoryPool fallback
Applying: Fix comments
Applying: Typos
Applying: Support increase buffer size
Applying: Add function tests, rename argument
error: sha1 information is lacking or useless (src/Servers/Kestrel/Core/src/Internal/Http/Http1OutputProducer.cs).
error: could not build fake ancestor
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0005 Add function tests, rename argument
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

sebastienros added a commit that referenced this pull request Sep 15, 2021
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ResponseCachingMiddleware: exception "Cannot allocate more than 4096 bytes in a single buffer" when a client disconnects
5 participants