-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Add GCHandle<T> #111307
base: main
Are you sure you want to change the base?
Add GCHandle<T> #111307
Conversation
Note regarding the
|
1 similar comment
Note regarding the
|
public void Dispose() | ||
{ | ||
// Free the handle if it hasn't already been freed. | ||
IntPtr handle = Interlocked.Exchange(ref _handle, IntPtr.Zero); | ||
if (handle != IntPtr.Zero) | ||
{ | ||
GCHandle.InternalFree(handle); | ||
} | ||
} |
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.
Different from GCHandle.Free
, the Dispose
methods are not throwing. This matches the contract of IDisposable
to allow duplicated dispose calls.
[CLSCompliant(false)] | ||
public static unsafe T* GetAddressOfArrayData<T>(this PinnedGCHandle<T[]?> handle) | ||
=> (T*)handle.GetAddressOfObjectData(); | ||
|
||
/// <summary> | ||
/// Retrieves the address of string data in a <see cref="PinnedGCHandle{T}"/> of <see cref="string"/>. | ||
/// </summary> | ||
/// <returns>The address of the pinned <see cref="string"/>.</returns> | ||
[CLSCompliant(false)] | ||
public static unsafe char* GetAddressOfStringData(this PinnedGCHandle<string?> handle) | ||
=> (char*)handle.GetAddressOfObjectData(); |
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.
Should these be nullable oblivious? Given there's no covariance, annotating either with nullable or non-nullable will give warning for the handles annotated with the opposite.
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.
We suggested making these nullable during API review under the assumption that that would allow both without warnings. If that's not actually the case (as in, calling this with a non nullable T
still produces a warning), making these nullable oblivious makes sense to me 🤔
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 will produce warning for non-nullable T
. PinnedGCHandle<string>
can't be converted to PinnedGCHandle<string?>
because Set(null)
will be warned for the former.
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.
Marking nullable oblivious will allow cause oblivious of T?*
and T*
when T
is managed type. I don't think it's a big deal since pointers to managed type is more unsafe and uncommon. Instead top level nullability will bother people more.
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.
What about only making the parameter nullable oblivious? Would that be doable?
As in:
public static unsafe T* GetAddressOfArrayData<T>(
#nullable disable
this PinnedGCHandle<T[]> handle)
#nullable restore
=> (T*)handle.GetAddressOfObjectData();
So we can preserve the nullability annotation on the returned pointer?
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.
While I'm not seeing any behavioral difference for type inference and nullability warning, the compiled metadata are different. Agreed that nullable oblivious range should be as short as possible.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
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.
Left a few notes based on previous discussions 🙂
Thank you for taking this one!
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PinnedGCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PinnedGCHandle.T.cs
Outdated
Show resolved
Hide resolved
get | ||
{ | ||
IntPtr handle = _handle; | ||
GCHandle.ThrowIfInvalid(_handle); |
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.
All these checks should be removed. Using any instance method on an instance that's not allocated is explicitly not supported. As long as we can throw an exception (eg. this should throw NRE) so the behavior is still consistent, that's sufficient.
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.
As long as we can throw an exception (eg. this should throw NRE)
This is the questionable part - the current implementation uses FCall in some cases, and the ability to throw exception from FCall will soon be removed. In other words, no NRE can be thrown, at least for some members.
With that said, providing invalid value via FromIntPtr
will still cause hard AV in unmanaged code.
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.
At least for the getter, on CoreCLR and NAOT this should just be *(T*)_handle
, which should already throw NRE implicitly.
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.
Maybe an implicit read (_ = *(byte*)value
) can be applied with lowest overhead? I'm not worried about garbage value from FromIntPtr
, but make new GCHandle<T>{ Target = obj }
crashing doesn't look good.
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.
What do you mean by implicit read? You need to read anyway, since you're returning a value. What I'm saying is the whole getter should literally just be get => *(T*)_handle
. That will automatically NRE if handle
is null
, which is all we need.
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 mean adding a read in managed code in setter. Currently the entire setter is executed in unmanaged code and will cause AV, which won't be caught as NRE.
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.
Oh I see what you mean, I thought you were talking about the other accessor. That would seem fine to me, but I'll defer to Jan and others to confirm 🙂
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandleExtensions.cs
Show resolved
Hide resolved
IntPtr handle = _handle; | ||
GCHandle.ThrowIfInvalid(handle); | ||
|
||
return GCHandle.AddrOfPinnedObjectFromHandle(_handle); |
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.
Is this expected to follow GCHandle in special casing strings and arrays here?
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.
Memory<T>.Pin
and fixed
are all following the contract to return the address of first element. Not following the contract would require strong justification.
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.
This method should be only used for non-array non-string blittable types. Memory<T>.Pin
and fixed
do not work for non-array non-string blittable types.
I think it would make sense to make it work just for the non-array non-string blittable types and document the behavior as undefined to avoid unnecessary overhead. The overall goal with these new GCHandle types was to eliminate unnecessary overheads.
The original intention with this method was to provide a replacement for GCHandle.AddrOfPinnedObject. I think the method has different enough name to have different behavior.
#nullable disable // Nullable oblivious because no covariance between PinnedGCHandle<T> and PinnedGCHandle<T?> | ||
this PinnedGCHandle<T[]> handle) | ||
#nullable restore | ||
=> (T*)handle.GetAddressOfObjectData(); |
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.
Should those be implemented separately here to avoid runtime checks GCHandle does for performance?
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.
Good point. Yes the check can't be eliminated here.
/// <exception cref="NullReferenceException">If the handle is not initialized or already disposed.</exception> | ||
[CLSCompliant(false)] | ||
public static unsafe T* GetAddressOfArrayData<T>( | ||
#nullable disable // Nullable oblivious because no covariance between PinnedGCHandle<T> and PinnedGCHandle<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.
@stephentoub Is #nullable disable
correct here?
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.
That will make the parameter nullable oblivious, which appears to be the goal.
And... that's desirable because we want to accept both nullable and non-nullable arrays as the type arg?
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.
Yeah that was the intent here. When I suggested making the T
nullable in API review I assumed that'd have allowed also passing a non nullable T
just fine, but apparently that also warns, because reasons. So we figured just making this nullable oblivious would fix that. And keeping that restricted to the parameter means at least you preserve and flow the nullability to the returned T
, rather than making the whole method nullable oblivious.
{ | ||
// | ||
// The delegate might already been garbage collected |
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.
// The delegate might already been garbage collected | |
// The delegate might have already been garbage collected |
...oreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs
Show resolved
Hide resolved
@@ -211,7 +211,6 @@ public MetadataReader Reader | |||
|
|||
byte[] metadataArrayTemp = _currentBinaryEmitter.EmitToMetadataBlob(); | |||
byte[] metadataArray = GC.AllocateArray<byte>(metadataArrayTemp.Length, pinned: true); | |||
System.Runtime.InteropServices.GCHandle.Alloc(metadataArray, System.Runtime.InteropServices.GCHandleType.Pinned); |
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.
This seems a bit odd given it isn't stored anywhere, but it is also going to be creating a side effect. Do we know if this is okay to remove?
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.
The array is pinned in a line above. This handle would be leaked permanently.
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.
Is it guaranteed that the array is kept alive for as long as the unmanaged pointer into the array is in use (the unmanaged pointer is acquired a few lines below)?
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.
Realized it's not that simple. The handle is also keeping the array alive.
I'd like to leave it as-is and address in the future. The usages of GCHandle and PEReader don't look consistent in R2R.
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.
Could it just be a normal handle instead though?
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.
That won't make any difference.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
/// or <see cref="GCHandleExtensions.GetAddressOfStringData(PinnedGCHandle{string})"/> instead. | ||
/// </para> | ||
/// <para> | ||
/// This method should only be used for blittable types. The layout of non-blittable types is undefined for interop. |
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.
/// This method should only be used for blittable types. The layout of non-blittable types is undefined for interop. | |
/// This method should only be used for blittable types. |
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.
Please commit this update.
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'm unsure about what to express to users here. Should "layout is not defined" be mentioned here? Is it discussed in interop documentation, can it be linked from this documentation?
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.
Should "layout is not defined" be mentioned here?
Nope. This is unnecessary for documentation on GCHandle
. If the intent here is to limit this to blittable types, unclear if that is a hard requirement, then state that. The layout of non-blittable types is possible to be defined too. One can apply the Sequential
to a class
with object
fields and its layout would be "defined".
What is the intent of this method? State what it is intended to do.
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.
One can apply the Sequential to a class with object fields and its layout would be "defined".
This makes the layout of the marshalled view defined. The layout of the pinned type is undefined.
Maybe this should say "The layout of pinned non-blittable types is undefined."?
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.
My understanding is that this API basically returns Unsafe.AsPointer(ref RuntimeHelpers.GetRawData(obj))
*, where RuntimeHelpers.GetRawData
is an API that is yet to be exposed publically, but returns a managed reference to the "first field" in the type (*: it is possible that we would make a publically exposed RuntimeHelpers.GetRawData
point to the first character of a string or first element in an array, instead of to some length/s, as suggested in the API proposal currently #28001).
In the case of a blittable type, this layout will match what you would get from marshalling. For unmanaged
, non-blittable types, it will not necessarily match what you get from marshalling (depending on something like DisableRuntimeMarshallingAttribute
I think); however this can still presumably be used from native if the actual layout matches what you have. (note: in these cases, it's still usually only useful for interop with something that is Sequential
layout obviously - you could imagine uses with Auto
layout still though, e.g., you might have a sequential layout field in an auto layout type & you add offset to that field & pass that pointer instead)
The layout of a type is undefined if its fields are not unmanaged
, but can still be meaningfully interacted with if we had something like #94976 (and can be calculated today, I just don't think we guarantee that field offsets don't change, nor that fields are stored within the object, so it's UB for now, but probably/hopefully not forever).
So, in conclusion, I would think that a comment like this is probably most accurate/useful: This method returns a pointer to the first field of the managed layout of the type. The unmanaged layout of the type is only guaranteed to match the managed layout for blittable types.
This does skip some nuance with explicit layout & stuff like DisableRuntimeMarshallingAttribute
, but we presumably don't want to go into every single bit of detail here(?). Even that is potentially a bit much detail, which is why This method should only be used for blittable types.
was done presumably, but I personally think this is a bit too little detail.
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.
For my understandings:
Only types with true-sequential/manual layout in managed memory should be used with this API. The managed layout is largely an implementation detail, and only blittable value type are guaranteed to have the same layout in unmanaged and managed memory, thus following the expectation in [StructureLayout]
.
There have been multiple questions about this and I think it worth a dedicated article to discuss the guarantees. For this member, it should focus on a clear definition of supported types.
Is "blittable value types" clear enough for this purpose?
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.
Is "blittable value types" clear enough for this purpose?
While blittable types (including boxed structs & classes, with sequential layout) are probably the only real uses when performing interop, presumably PinnedGCHandle<T>
can be used for other cases & GetAddressOfObjectData
could be useful there, so I don't think this is a useful definition, unless it's made clear that it is only a relevant consideration when passing the pointer to native for something like interop. I think the comment I have in my comment is probably more encompassing of all possible uses of the pointer & more accurate.
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.
This method returns a pointer to the first field of the managed layout of the type.
I do not think that this valid statement. For example, if you have a type with a single field added via Edit and Continue, this API is not going to return a pointer to the first field.
Is "blittable value types" clear enough for this purpose?
I do not think so. The primary use case for this API are blittable reference types. Reference types are not value types.
I think it should say "blittable types"
presumably PinnedGCHandle can be used for other cases & GetAddressOfObjectData could be useful there
This is complex uncharted territory. I do not think we want to be defining as side-effect of introducing this API.
I know that this API is controversial. It is why it was not included in the original proposal. I think omitting it from this PR would be a perfectly fine option.
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 have committed the update. I agree with Aaron that "undefined for interop" note was unnecessary here.
If we wanted to link to a doc with more information, I think it would be https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types .
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/WeakGCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/WeakGCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/WeakGCHandle.T.cs
Outdated
Show resolved
Hide resolved
@@ -734,7 +734,6 @@ private async Task CopyToAsyncInternal(Stream destination, int bufferSize, Cance | |||
finally | |||
{ | |||
CryptographicOperations.ZeroMemory(rentedBuffer.AsSpan(0, bufferSize)); |
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 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 don't think the lifetime extension is a big concern. The point of the pin is to make sure the GC doesn't copy it around (move it). As long as the ZeroMemory
happens before the pin is let go, I think it's fine. However:
- It seems a little odd to return a rented buffer while it is still pinned.
System.Security
prefers using explicit scopes forusing
.
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.
Or, more specifically, since there is a brief window of time while a pinned buffer has been returned to the pool, what happens if thread A returns a pinned buffer, thread B rents it and tries to pin it, then thread A unpins it?
It seems like the most sensible thing to do is just make sure it unpinned before it gets returned.
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.
Shouldn't multiple pinning handles working simultaneously? In other words, when there's at least 1 pinning handle, the array should be pinned.
Agree that the pinning period should just over clearing.
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.
Yeah, this should just undo the using
and keep the Free/Dispose where it was. It's already a try/finally, we don't need a try/finally wrapping the try/finally.
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.
using
multiple variables would also create nested try/finally. It should be acceptable pattern.
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.
@huoyaoyuan Please revert this change by the request of the code owner, @bartonjs.
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.
The using
specifically. I think the nullable change is appropriate.
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.
Copilot reviewed 10 out of 25 changed files in this pull request and generated no comments.
Files not reviewed (15)
- src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj: Language not supported
- src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems: Language not supported
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/UnsafeGCHandle.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Unix.cs: Evaluated as low risk
- src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs: Evaluated as low risk
- src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs: Evaluated as low risk
- src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventProvider.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs: Evaluated as low risk
- src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs: Evaluated as low risk
- src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs: Evaluated as low risk
- src/libraries/System.Private.CoreLib/src/System/IO/PinnedBufferMemoryStream.cs: Evaluated as low risk
Comments suppressed due to low confidence (3)
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PinnedGCHandle.T.cs:86
- Ensure that the GetAddressOfObjectData method is adequately tested, especially for blittable types.
public readonly unsafe void* GetAddressOfObjectData()
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PinnedGCHandle.T.cs:125
- Verify that the Dispose method is properly tested to ensure that resources are correctly released.
public void Dispose()
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs:106
- Add a test case to verify the correctness of the Equals method for GCHandle.
public readonly bool Equals(GCHandle<T> other) => _handle == other._handle;
{ | ||
// | ||
// The delegate might already been garbage collected |
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.
"might have"... what situation would result in TryGetTarget being false here but because of some reason other than GC?
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.
The comment was copied from old one. Maybe it's saying in the context of the whole method?
/// <param name="target">The object that uses the <see cref="GCHandle{T}"/>.</param> | ||
public GCHandle(T target) | ||
{ | ||
_handle = GCHandle.InternalAlloc(target, GCHandleType.Normal); |
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.
Since we rely on IntPtr.Zero elsewhere in the implementation having special meaning, worth asserting here that _handle != IntPtr.Zero?
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.
Should the assert go to InternalAlloc
since there are more types depending on this?
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandleExtensions.cs
Show resolved
Hide resolved
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/PinnedGCHandle.T.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/WeakGCHandle.T.cs
Outdated
Show resolved
Hide resolved
…pServices/PinnedGCHandle.T.cs Co-authored-by: Aaron Robinson <[email protected]>
// Pin the array for security. | ||
GCHandle pinHandle = GCHandle.Alloc(rentedBuffer, GCHandleType.Pinned); | ||
using PinnedGCHandle<byte[]> pinHandle = new PinnedGCHandle<byte[]>(rentedBuffer); |
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.
using PinnedGCHandle<byte[]> pinHandle = new PinnedGCHandle<byte[]>(rentedBuffer); | |
PinnedGCHandle<byte[]> pinHandle = new PinnedGCHandle<byte[]>(rentedBuffer); |
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've reverted all changes in CryptoStream
.
@@ -734,7 +734,6 @@ private async Task CopyToAsyncInternal(Stream destination, int bufferSize, Cance | |||
finally | |||
{ | |||
CryptographicOperations.ZeroMemory(rentedBuffer.AsSpan(0, bufferSize)); |
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.
CryptographicOperations.ZeroMemory(rentedBuffer.AsSpan(0, bufferSize)); | |
CryptographicOperations.ZeroMemory(rentedBuffer.AsSpan(0, bufferSize)); | |
pinHandle.Dispose(); |
Any further review on this? |
Closes #94134.
IEquatable<T>
included.