Skip to content
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

The cache used by CompileNonGenericCustomTypeMapper is not thread-safe #150

Open
bvandoren opened this issue Feb 22, 2018 · 2 comments · May be fixed by #158
Open

The cache used by CompileNonGenericCustomTypeMapper is not thread-safe #150

bvandoren opened this issue Feb 22, 2018 · 2 comments · May be fixed by #158

Comments

@bvandoren
Copy link

I am seeing the following NullReferenceException thrown from my web service application:

System.NullReferenceException: Object reference not set to an instance of an object.
  at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
  at ExpressMapper.MappingServiceProvider.CompileNonGenericCustomTypeMapper(Type srcType, Type dstType, ICustomTypeMapper typeMapper, Int64 cacheKey)
  at ExpressMapper.MappingServiceProvider.MapNonGenericInternal(Type srcType, Type dstType, Object src, Object dest, Boolean dynamicTrial)
  at ExpressMapper.MappingServiceProvider.Map[T,TN](T src)
  ...

I believe that the root cause is a race condition that gets exercised in the following scenario.

Let's say I have the following classes and interface

public interface IFoo { … }

public class CoreFoo : IFoo { … }

public class ApiFoo { … }

And a custom mapping registration like

Mapper.RegisterCustom<IFoo, ApiFoo>(…);

Which I use with the following call to Map

var foo = new CoreFoo(…);
return foo.Map<IFoo, ApiFoo>();

Since the runtime type of foo is not IFoo (it's CoreFoo), my call to Map method will call into MapNonGenericInternal. The non-generic method finds my custom registration for IFoo -> ApiFoo and attempts to compile and cache an Expression for invoking the custom mapping in this block. The cache it uses is a Dictionary, which is not thread safe under modification. So, here we have a possible race condition wherein multiple threads have a cache miss for the mapping above, attempt to Insert the newly compiled Expression to the cache, and one thread fails as the Dictionary has already been modified by some other thread. As best as I can tell, a NullReferenceException is a possibility when two threads attempt to modify a Dictionary concurrently.

I can work around the problem on my end by changing my registration or synchronizing this particular call to Map. For a long term solution, is it possible to make the usage of this cache thread safe from your end? Say with a ConcurrentDictionary? Let me know if you need more information.

@mitikov
Copy link

mitikov commented Dec 17, 2021

Same for us, #157

@mitikov
Copy link

mitikov commented Dec 22, 2021

Fixing by #158

@mitikov mitikov linked a pull request Dec 24, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants