-
Notifications
You must be signed in to change notification settings - Fork 485
Description
@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:
-
Do nothing.
That is, leave it up to downstream libraries to ensure that if they accessModuleScope'sModuleBuilders, they must not call into theirProxyGeneratorat the same time. -
Deprecate
ModuleScope.DefineTypeas well.
Not very realistic because, for instance, NSubstitute and Moq both rely on this API in order to mock delegate types. -
Revert Deprecate
LockandModuleScope'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. -
Expose the
ReaderWriterLockSlimlock thatModuleScopeuses internally.
ModuleScopehas an API that exposes the twoModuleBuilders that it uses, which essentially means that once a user starts accessingModuleBuilders, all thread-safety guarantees are off. We can no longer expose aLock, but we could expose the internalReaderWriterLockSliminstead. -
Augment
ModuleScopewith a thread-safe alternative forDefineType.
Apart from (0) above, this is my personal preference. This could be in the form of a new method onModuleScopewhich 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
ObtainDynamicModuleAPI as well.ModuleScopewould 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?