-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Adding MemoryHandle AddOffset to fix Memory.Retain impl #24323
Conversation
@@ -178,7 +178,8 @@ public unsafe struct MemoryHandle : IDisposable | |||
{ | |||
public MemoryHandle(IRetainable owner, void* pinnedPointer = null, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle)) { throw null; } | |||
public void* PinnedPointer { get { throw null; } } | |||
public void Dispose() { throw null; } | |||
public void AddOffset(int offset) { throw 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.
Has this been API reviewed? It feels strange to me to mutate the MemoryHandle in this fashion, and we have Unsafe.Add in case someone wants to work with the pointer. If this is purely to help with the implementation of Memory, maybe it should just be internal?
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.
If this is purely to help with the implementation of Memory, maybe it should just be internal?
I agree. I think this could be an internal API that is only needed by Memory.
@KrzysztofCwalina, do you see a scenario where we would need to expose the AddOffset API publically?
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.
(And if for some reason its functionality must be public, I'd prefer if we just made PinnedPointer settable.)
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.
Yes, the scenario is I use OwnedMemory and MemoryHandle directly, without using Memory, e.g. I implement my own Memory-like factory for spans. If we don't like AddOffset, we should add OwnedMemory.Pin overload that takes offset to the added to the pointer inside the handle.
Having said that, we need to fix this bug today, and so possibly an internal API is a better change short term so that we don't have to rush the API review.
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.
Having said that, we need to fix this bug today, and so possibly an internal API is a better change short term so that we don't have to rush the API review.
The CI system is still down it seems. I am not sure if we can get this fix merged today.
cc @mmitche
@@ -193,6 +193,7 @@ public unsafe MemoryHandle Retain(bool pin = false) | |||
if (_index < 0) | |||
{ | |||
memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin(); | |||
memoryHandle.AddOffset((_index & RemoveOwnedFlagBitMask) * Unsafe.SizeOf<T>()); |
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.
It feels wrong to me to expose a public method like AddOffset for mutating the handle. I think I'd prefer either:
- Just have AddOffset be internal, or replace it with an internal SetPointer and use Unsafe.Add to compute the new one
- Create a new MemoryHandle with the inputs from the one returned by Pin, and just don't dispose the first one.
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.
I agree, creating new memory handle sound like much better idea and does not require any API change.
@dotnet-bot test this please |
looks good. Separately, please file a bug to change the Pin APIs to take an index (or some other solution to let code not using Memory do the offset. |
@dotnet-bot test Linux x64 Release Build |
@dotnet-bot test UWP CoreCLR x64 Debug Build |
Need to merge dotnet/coreclr#14248 before CI here will be green/this PR can be merged. |
@dotnet-bot test Linux x64 Release Build |
@KrzysztofCwalina, I believe we need the following PR to get merged before the CI will pass: |
#24349 won't help in its current state. It is not updating CoreCLR. |
Ah yes, good point. How long does it usually take to trigger a PR after a version gets updated in the version repo? |
@dotnet-bot test Linux x64 Release Build |
@dotnet-bot test Windows x64 Debug Build |
@dotnet-bot test OSX x64 Debug Build |
@@ -178,7 +178,8 @@ public unsafe struct MemoryHandle : IDisposable | |||
{ | |||
public MemoryHandle(IRetainable owner, void* pinnedPointer = null, System.Runtime.InteropServices.GCHandle handle = default(System.Runtime.InteropServices.GCHandle)) { throw null; } | |||
public void* PinnedPointer { get { throw null; } } | |||
public void Dispose() { throw null; } | |||
internal void AddOffset(int offset) { throw 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.
Why does an internal method need to be in the ref?
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.
I suppose it doesn't.
I will remove internal methods in the ref in other places as well.
- https://github.com/dotnet/corefx/blob/master/src/System.Memory/ref/System.Memory.cs#L131
- https://github.com/dotnet/corefx/blob/master/src/System.Memory/ref/System.Memory.cs#L203
- etc.
Are internal constructors treated uniquely? I see a lot of internal constructors in the S.Runtime ref.
https://github.com/dotnet/corefx/blob/master/src/System.Runtime/ref/System.Runtime.cs#L209
There are some other places that have internal APIs in the ref. Should they be there?
https://github.com/dotnet/corefx/blob/master/src/System.Threading/ref/System.Threading.cs#L139
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.
There are some other places that have internal APIs in the ref. Should they be there?
The only reason internal methods would need to be there is if some other library has InternalsVisibleTo access and is accessing them. We've tried to eliminate all such usage, though maybe there are still a few stragglers.
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.
Wouldn't the InternalsVisibleTo apply only to the source projects? Why would that require internals to be listed in the reference assembly?
I mainly see InternalsVisibleTo for the test project related to the library itself: https://github.com/dotnet/corefx/search?p=1&q=InternalsVisibleTo&type=&utf8=%E2%9C%93
But in some cases (like the one I linked), I don't see any library that is accessing this internal API (other than the test):
https://github.com/dotnet/corefx/blob/master/src/System.Threading/ref/System.Threading.cs#L139
public partial class HostExecutionContext : System.IDisposable
{
public HostExecutionContext() { }
public HostExecutionContext(object state) { }
protected internal object State { get { throw null; } set { } }
public virtual System.Threading.HostExecutionContext CreateCopy() { throw null; }
public void Dispose() { }
public virtual void Dispose(bool disposing) { }
}
However, if I remove it from the ref, I get APICompat error:
error : MembersMustExist : Member 'System.Threading.HostExecutionContext.State.get()' does not exist in the implementation but it does exist in the contract.
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.
Are internal constructors treated uniquely
Yes. The C# compiler generates an implicit public constructor if you do not have any explicit constructor. If you do not want to have a public constructor, you need to have internal one - it is the purpose of the internal constructors in S.Runtime ref.
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.
However, if I remove it from the ref,
What did you remove, just the internal keyword, or the whole property? "protected internal" means "protected or internal", not "protected and internal", so a "protected internal" property is visible outside of the assembly as protected.
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.
If you do not want to have a public constructor, you need to have internal one - it is the purpose of the internal constructors in S.Runtime ref.
Got it. That makes sense.
What did you remove, just the internal keyword, or the whole property? "protected internal" means "protected or internal", not "protected and internal", so a "protected internal" property is visible outside of the assembly as protected.
I had removed the whole property (thinking it was "and"). Thanks for the clarification.
* Adding MemoryHandle AddOffset to fix Memory.Retain impl * Adding AddOffset to System.Runtime ref. * Make the AddOffset API internal.
…x#24323) * Adding MemoryHandle AddOffset to fix Memory.Retain impl * Adding AddOffset to System.Runtime ref. * Make the AddOffset API internal. Commit migrated from dotnet/corefx@b9676be
Fixes https://github.com/dotnet/corefx/issues/24317
Related PR: dotnet/coreclr#14248
cc @pakrym, @KrzysztofCwalina, @stephentoub, @davidfowl, @jkotas