-
Notifications
You must be signed in to change notification settings - Fork 523
One-time leased blocks should not be tracked on Return #546
Conversation
Hi @clrjunkie, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! TTYL, DNFBOT; |
Looking to drop one time leased blocks in #525 |
ok.. just started looking at Kestrel so I can't comment, this is something that just caught my attention as an oversight. |
There are a lot of places in Kestrel where internal APIs don't have checks to prevent misuse. On one hand it is scary since if one-time leased blocks were ever returned. That would break some assumptions and possibly lead to some very subtle bugs that could be hard to track down. On the other hand, if we always validated parameters in low level code like this, there would be perf consequences. Even without #525 being merged, the code that returned the block was @clrjunkie Have you noticed |
@halter73 Frankly speaking, I just cloned the repo and started looking at the code. I picked the MemoryPool module as a random starting point :) so I don’t know what intrinsic relationships exists with the other modules… (perhaps I will eventually) in any case this module seems well encapsulated to require a robust interface... just my 2 cents. |
@clrjunkie I hear you. I do feel a little silly bringing up the perf impact of null checking a property. I'll grant that there likely isn't a significant perf impact for this one change even in our plaintext benchmarks. However this isn't the first time that I've considered adding a null check for this exact property, and I've already decided against it. MemoryPoolIterator2.CopyFrom would also break if the backing block is not from a Pool, but in practice it always is from a pool. We could have added a null check for that (and a few other things) to make the Instead we decided to use I understand wanting to make the memory pool interface robust, but adding a bunch of checks like these in all the places where they could be added would have a negative perf impact. I do think that these classes should probably be made internal though, since they aren't really designed for public consumption. |
Good to know! (I didn't want the pull request to be considered as too "offensive" :)
Why? Maybe I like ;) |
Mostly because the design so far is dictated by the needs of Kestrel without really any consideration for a wider audience. It's all kinda hidden in the The only reason the thing is called |
Oh. And don't worry. I don't offend to easily. I (almost) always like hearing other perspectives on things. |
Actually, I think leaving the null check would be better, since the decision on whether to track the block or not is an implementation detail that is not visible to the caller. The caller must always return the allocation to the Pool regardless, that way the interface can be kept simple. |
If you call In the few cases that we do provide a minimum size, you should check the |
I suggest you reconsider.. this is one more thing to remember and calls for oversights possibly by other developers that would use this facility to support other / future kestrel features, especialy when it can be easly avoided with virtualy zero perf impact. |
Alright, I'll merge it. How about instead of checking I think |
The reason I tested Slab was to stay compatible with the destruction pre-condition ~MemoryPoolBlock2()
Debug.Assert(block.Pool == this) won't work for one-time leases since pool is always null return MemoryPoolBlock2.Create( Therefore a ref assignment would be required. Other than that Assert followed by Pool test sounds fine. |
As far as the destructor goes I think that's mostly designed to support the currently unimplemented shrinking of the memory pool. The correct thing to do to support that would be to check |
but this will mask one time user returns to an incorrect pool.. the interface need to be consistent about this... a bug is a bug. |
I don't understand. The point of the assert is so that it's not masked if the user returns a block to the wrong pool. Today, it the code won't report the error in any way. |
Are you suggesting we always throw an exception instead of using Debug.Assert? |
One-time MemoryPoolBlock2 Leases are currently created by the following code: return MemoryPoolBlock2.Create(
new ArraySegment<byte>(new byte[minimumSize]),
dataPtr: IntPtr.Zero,
pool: null,
slab: null); Notice that the pool parameter is assigned null. If we add an Assert in the Return method in order to check that a block is actually returned to the right pool than the following won’t work since an Assert will be raised regardless if the caller returned the block to the right pool or not
Changing the order effectively means that the check is by-passed for One-time MemoryPoolBlock2 Leases because currently One-time MemoryPoolBlock2 leases are not associated with any Pool. For example, the following will always work: var poolA = new MemoryPool2()
var poolB = new MemoryPool2()
var block = poolA.Lease(10000);
poolB.Return(block) // This is a caller bug but it won’t be Asserted Because: block.Pool == null // is true and (block.Pool != null)
{
Debug.Assert(block.Pool == this)
} The fact that we are simply discarding the block in the Return method is an internal implementation detail that is of no concern to the caller and may very well change – the caller must always do the right thing which is to return the allocated Block to the right Pool. So in order to correctly implement this we also need to store a reference to the pool from which the One-time MemoryPoolBlock2 Lease was "leased" if (minimumSize > _blockLength)
{
return MemoryPoolBlock2.Create(
new ArraySegment<byte>(new byte[minimumSize]),
dataPtr: IntPtr.Zero,
pool: this,
slab: null);
} notice pool is assigned this |
Frankly, I wasn't that concerned about catching every logic error when I suggested adding the I like the idea always setting the Still, I would double check all references to Since always setting |
So how would you like to go about this? all changes in a new PR? separate PR for each work item? |
I would just do it all in this PR. |
Ok. tomorrow (at a completely different timezone) Any tips on the GitHub process to augment this existing PR? |
Sounds good. You should be able to continue pushing the the existing branch you made the PR from. |
@halter73 done. |
Can you please remove clrjunkie@5e14e55 from this PR?
If you could, also squash all your remove null-conditional operator commits, that would be great. Thanks! |
You are correct. I'll fix. |
Done. |
@@ -597,8 +597,7 @@ public void CopyFrom(ArraySegment<byte> buffer) | |||
|
|||
public void CopyFrom(byte[] data, int offset, int count) | |||
{ | |||
Debug.Assert(_block != null); | |||
Debug.Assert(_block.Pool != null); | |||
Debug.Assert(_block != null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: you have spaces following the semicolon on these lines.
Looks good other than the trailing spaces thing. |
@halter73 Any idea why this failing? |
Needs rebasing?
|
Though you may need to switch to your
|
@benaadams Thanks, I'm on a different computer and I simply cloned my fork C:\clrjunkie\KestrelHttpServer>git remote -v |
Might want to pull |
clrjunkie@5dfc268 should not be included in this PR. Instead you should use I'm happy to do this for you and close this PR, but I understand if you want to figure this out yourself. |
8bc057c
to
e963cc7
Compare
…that a block is returned to it's source pool / Managed block are only returned to active Slabs
@halter73 @benaadams Thanks. @halter73 BTW, there are only 6 removals null-conditional operators. The 7th was removed in commit afe944c |
@clrjunkie Thanks! |
No problem... It was a good exercise :) |
Is there any reason too?