Skip to content

Do we need a thread-safe alternative to ModuleScope.DefineType? #399

@stakx

Description

@stakx

@zvirja alerted me to the fact that after deprecating Lock (in #391), it seems no longer possible in the layers above DynamicProxy to use ModuleScope.DefineType in a thread-safe manner; see https://github.com/nsubstitute/NSubstitute/pull/420/files#r197765187.

I see three options to resolve this:

  1. Do nothing.
    That is, leave it up to downstream libraries to ensure that if they access ModuleScope's ModuleBuilders, they must not call into their ProxyGenerator at the same time.

  2. Deprecate ModuleScope.DefineType as well.
    Not very realistic because, for instance, NSubstitute and Moq both rely on this API in order to mock delegate types.

  3. Revert Deprecate Lock and ModuleScope's public type cache API #391.
    I'm biased here, but I think it would be a pity because that PR went in the right direction of closing doors into DynamicProxy's internals.

  4. Expose the ReaderWriterLockSlim lock that ModuleScope uses internally.
    ModuleScope has an API that exposes the two ModuleBuilders that it uses, which essentially means that once a user starts accessing ModuleBuilders, all thread-safety guarantees are off. We can no longer expose a Lock, but we could expose the internal ReaderWriterLockSlim instead.

  5. Augment ModuleScope with a thread-safe alternative for DefineType.
    Apart from (0) above, this is my personal preference. This could be in the form of a new method on ModuleScope which performs the necessary locking internally, for example:

    +[Obsolete("This method is not thread-safe. Please use `ModuleBuilder.CreateType` instead.")]
     public TypeBuilder DefineType(bool inSignedModulePreferably, string name, TypeAttributes flags)
     {
         …
     }
    
    +public Type CreateType(bool inStrongNamedModulePreferably,
    +                       string name,
    +                       TypeAttributes flags,
    +                       Func<TypeBuilder, Type> build)
    +{
    +    TypeCache.Lock.EnterWriteLock();
    +    try
    +    {
    +        var module = ObtainDynamicModule(disableSignedModule == false && inStrongNamedModulePreferably);
    +        var typeBuilder = module.DefineType(name, flags);
    +        return build.Invoke(typeBuilder);
    +    }
    +    finally
    +    {
    +        TypeCache.Lock.ExitWriteLock();
    +    }
    +}

    (This option would probably entail deprecating the ObtainDynamicModule API as well. ModuleScope would eventually have a less powerful API, but one where thread-safety would already be taken care of for downstream libraries.)

@castleproject/committers, do you see any other options here? If not, which one of the above do you think we should go for?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions