Skip to content

[API Proposal]: GCHandle<T> (like GCHandle, but this time it's great™️) #94134

Closed
@Sergio0694

Description

@Sergio0694

This API proposal is extracted from #94113 (comment) and supersedes/fixes:

Overview

The GCHandle type currently has a number of issues/inefficiencies, that are impossible to completely fix without introducing some (major) breaking changes, which could cause all sorts of problems for people having a dependency on the current behaviors. It would be beneficial to instead add a new handle type, which solves all of these problems from the start:

  • Unnecessary validation check (null check) when getting/setting targets
  • Pin flag removal every time the handle target is resolved
  • Interlocked operation from Free
  • Not implementing IDisposable

We have a reference implementation which can be a starting point for this.

cc. @jkotas @AaronRobinsonMSFT @tannergooding

API Proposal

namespace System.Runtime.InteropServices;

public struct GCHandle<T> : System.IDisposable
    where T : class
{
    public GCHandle(T? value, GCHandleType type = GCHandleType.Normal);

    public readonly bool IsAllocated { get; }
    public T? Target { readonly get; set; }

    public static GCHandle<T> FromIntPtr(IntPtr value);
    public static IntPtr ToIntPtr(GCHandle<T> value);

    public void Dispose();
}

public struct PinnedGCHandle<T> : System.IDisposable
    where T : class
{
    public PinnedGCHandle(T? value);

    public readonly bool IsAllocated { get; }
    public T? Target { readonly get; set; }

    public static PinnedGCHandle<T> FromIntPtr(IntPtr value);
    public static IntPtr ToIntPtr(PinnedGCHandle<T> value);

    public void Dispose();
}

public static class GCHandleExtensions
{
    public static T* AddrOfArrayData<T>(this PinnedGCHandle<T[]> handle);
    public static char* AddrOfStringData(this PinnedGCHandle<string> handle);
}

Additional caveats

  • Calling Target on a handle that's not allocated will throw NullReferenceException
  • Calling AddrOf* on a handle that's not allocated will throw NullReferenceException
  • Calling AddrOf* when the target is null will return a null pointer
  • Converting between the two handles via FromIntPtr/ToIntPtr if the handle type is mismatched is undefined behavior
  • Dispose is not thread safe (but still no-op after the first call, just not in a thread-safe manner)

API Usage

Same as GCHandle, really:

// In a constructor (or wherever)
this.handle = new GCHandle<MyObject>(myObj);

// Later on
MyObject? target = this.handle.Target;

Alternative Designs

The alternative would be to change GCHandle, but as mentioned, it seems impossible to fix all issues with back-compat.

Risks

Not really any risk, since this would be a brand new type.

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Runtime.InteropServicesin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

Status

No status

Relationships

None yet

Development

No branches or pull requests

Issue actions