Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Adding MemoryHandle AddOffset to fix Memory.Retain impl #24323

Merged
merged 3 commits into from
Oct 2, 2017

Conversation

ahsonkhan
Copy link

@ahsonkhan ahsonkhan commented Sep 29, 2017

Fixes https://github.com/dotnet/corefx/issues/24317

  • AddOffset throws if PinnedPointer is null. Should it fail silently and do nothing?
  • Would it be better to create a setter for PinnedPointer instead of the AddOffset API?

Related PR: dotnet/coreclr#14248

cc @pakrym, @KrzysztofCwalina, @stephentoub, @davidfowl, @jkotas

@@ -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; }
Copy link
Member

@stephentoub stephentoub Sep 29, 2017

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?

Copy link
Author

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?

Copy link
Member

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.)

Copy link
Member

@KrzysztofCwalina KrzysztofCwalina Sep 29, 2017

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.

Copy link
Author

@ahsonkhan ahsonkhan Sep 29, 2017

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>());
Copy link
Member

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.

Copy link

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.

@ahsonkhan
Copy link
Author

@dotnet-bot test this please

@KrzysztofCwalina
Copy link
Member

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.

@ahsonkhan
Copy link
Author

@dotnet-bot test Linux x64 Release Build
@dotnet-bot test Windows x64 Debug Build
@dotnet-bot test Windows x86 Release Build

@ahsonkhan
Copy link
Author

@dotnet-bot test UWP CoreCLR x64 Debug Build

@ahsonkhan
Copy link
Author

Need to merge dotnet/coreclr#14248 before CI here will be green/this PR can be merged.

@KrzysztofCwalina
Copy link
Member

@dotnet-bot test Linux x64 Release Build

@ahsonkhan
Copy link
Author

ahsonkhan commented Sep 30, 2017

@KrzysztofCwalina, I believe we need the following PR to get merged before the CI will pass:
#24349

@jkotas
Copy link
Member

jkotas commented Sep 30, 2017

#24349 won't help in its current state. It is not updating CoreCLR.

@ahsonkhan
Copy link
Author

#24349 won't help in its current state. It is not updating CoreCLR.

Ah yes, good point.
The coreclr version just got updated in the versions repo ~11 minutes ago:
dotnet/versions@a330a6b#diff-5f6099c37f777c410c4397b3f1e38870

How long does it usually take to trigger a PR after a version gets updated in the version repo?

@ahsonkhan
Copy link
Author

@dotnet-bot test Linux x64 Release Build

@ahsonkhan
Copy link
Author

@dotnet-bot test Windows x64 Debug Build
@dotnet-bot test Windows x86 Release Build

@ahsonkhan
Copy link
Author

@dotnet-bot test OSX x64 Debug Build
@dotnet-bot test UWP CoreCLR x64 Debug Build

@ahsonkhan ahsonkhan merged commit b9676be into dotnet:master Oct 2, 2017
@ahsonkhan ahsonkhan deleted the FixRetainBug branch October 2, 2017 21:18
@@ -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; }
Copy link
Member

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?

Copy link
Author

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.

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

Copy link
Member

@stephentoub stephentoub Oct 6, 2017

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.

Copy link
Author

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.

Copy link
Member

@jkotas jkotas Oct 6, 2017

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.

Copy link
Member

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.

Copy link
Author

@ahsonkhan ahsonkhan Oct 6, 2017

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.

@karelz karelz added this to the 2.1.0 milestone Oct 11, 2017
pjanotti pushed a commit to pjanotti/corefx that referenced this pull request Oct 31, 2017
* Adding MemoryHandle AddOffset to fix Memory.Retain impl

* Adding AddOffset to System.Runtime ref.

* Make the AddOffset API internal.
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…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
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants