Skip to content

[API Proposal]: New custom type marshaller features for pay-for-play features #69403

@jkoritzinsky

Description

@jkoritzinsky

Background and motivation

After getting some community feedback around the new source generated marshaller, we have decided that we want to add a few additional feature flags that the generator will respect to generate code differently. None of these features require us to change the shape of the marshaller types; the only change in code is emitted by the generator itself.

The NeedsKeepAlive flag will direct source-generated interop to emit a call to keep the marshaller object alive across the native call (though GC.KeepAlive or other means). This feature is used internally in the generator by delegate marshalling and can be used by many other marshallers as well (the internal HandleRefMarshaller comes to mind as well as the many uses of GC.KeepAlive in WinForms.

The RequiresPreservedState feature flag contributes to #69096. Ensuring that we preserve marshaller state between the managed->unmanaged and the unmanaged->managed stages in all scenarios is very expensive, and the majority of marshallers do not require this support. This feature flag provides an opt-in model to preserve state when required (with any additional required overhead) and will direct the generator to emit a diagnostic when preserving state is not supported.

The GuaranteedUnmarshal flag directs the generator to emit the ToManaged call on the marshaller to be generated in the GuaranteedUnmarshal phase in the code-gen (in the finally block in the LibraryImport case). This flag would be used by marshallers like the (currently intrinsic in the marshaller) SafeHandle marshaller that have certain guarantees around unmarshalling to avoid memory leaks if another marshaller throws an exception. Adding this flag (along with RequiresPreservedState should enable us to safely convert the SafeHandle marshalling code to be a public marshaller instead of being intrinsically coded into the generator (which we believe makes the source generator much more maintainable).

API Proposal

namespace System.Runtime.InteropServices.Marshalling;

[Flags]
public enum CustomTypeMarshallerFeatures
{
    None = 0,
    UnmanagedResources = 0x1,
    CallerAllocatedBuffer = 0x2,
    TwoStageMarshalling = 0x4,
+   NeedsKeepAlive = 0x8,
+   RequiresPreservedState = 0x10,
+   GuaranteedUnmarshal = 0x20
}

API Usage

[CustomTypeMarshaller(typeof(TManaged), Features = CustomTypeMarshallerFeatures.NeedsKeepAlive | CustomTypeMarshallerFeatures.RequiresPreservedState | CustomTypeMarshallerFeatures.GuaranteedUnmarshal)]
public struct Marshaller
{
// ... required members
}

Alternative Designs

No response

Risks

We only have a limited number of feature flags possible with the 32-bit CustomTypeMarshallerFeatures enum, so we need to make sure we don't use up all possible options.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions