-
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 Dispose/IDisposable to GCHandle #54792
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Based on the conversation in #19459 I think there is enough to move onto an API review. |
I assume the behavior / implementation is just: public void Dispose() => Free(); |
@stephentoub I think it would check if the handle was allocated first. public void Dispose()
{
if (IsAllocated)
Free();
} |
Yup. Thanks. |
This implementation is both not thread safe (you cannot safely call it from multiple threads and assuming first one wins) and slower than necessary (pays extra for the interlocked compare change inside Free). I think we would either want thread safe implementation; or non-thread safe fast implementation (w/o interlocked compare exchange). |
Yes, sorry, I was just trying to ask about the semantics of what Dispose did, rather than actually implement it in a comment. From a thread-safety perspective, I think it'd be really confusing/error-prone if Free and Dispose had different thread-safety guarantees, so my preference would be to have Dispose use CompareExchange, e.g. IntPtr handle = Interlocked.Exchange(ref _handle, IntPtr.Zero);
if (handle != IntPtr.Zero)
InternalFree(GetHandleValue(handle)); |
Is As a quick poll: var m = MyStruct.Alloc();
using (m)
{
}
Console.WriteLine(m);
struct MyStruct : IDisposable
{
private bool _isUsed;
public static MyStruct Alloc() => new MyStruct { _isUsed = true };
public void Dispose()
{
_isUsed = false;
}
public override string ToString()
{
return $"IsUsed: {_isUsed}";
}
} People whose intuition says this is going to print |
We do it in other advanced places that have a similar potential for misuse, e.g. |
Note that the thread-safety guarantee in Free is nearly useless. I have not ever seen a code that would take advantage of it - such code would have to catch the InvalidOperationException that gets thrown when the handle has been freed already. |
If we really believe it's useless, then we should remove it from both. My point is that I think Free and Dispose should make the same guarantees. |
namespace System.Runtime.InteropServices
{
public struct GCHandle : IDisposable
{
public void Dispose();
}
} |
Given some of the fallout from #55596, I think we need to consider the impact of this change. Moving back to API needs work. |
I don't think the issue with xunit should block this API. This could happen when any type whatsoever starts implementing |
Superseded by #94134, closing this. |
Proposed API
Usage Examples
Risks
Copying/boxing a
GChandle
value and callingDispose
on each copy will still possibly result in a crash. This is the same behavior as other approved APIs already though (eg.DependentHandle
), and it's a limitation with C# not having proper move semantics.The text was updated successfully, but these errors were encountered: