Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullReferenceException when adding to Dictionary in Asynchronous context

I am having a very strange behaviour as in this picture:

enter image description here

As you can see in the Watch window, everything that can be possibly null are not null.

Here is the full code of the function:

    public LogInTokenCache GetUserIdFromToken(string token)
    {
        LogInTokenCache item = null;

        if (this.TokenCache.ContainsKey(token))
        {
            item = this.TokenCache[token];

            if (item.ExpirationTime < DateTime.Now)
            {
                this.TokenCache.Remove(item.Token);
                return null;
            }
        }
        else
        {
            LogInTokenBusiness tokenBusiness = new LogInTokenBusiness();

            var entity = tokenBusiness.FindToken(token);
            if (entity != null && entity.Token != null)
            {
                item = new LogInTokenCache()
                {
                    Token = entity.Token,
                    UserID = entity.UserId,
                    ExpirationTime = entity.ExpirationTime,
                };

                this.TokenCache.Add(item.Token, item);
            }
        }

        return item;
    }

I used Find All References feature, and this is the only where I use the Constructor as well as a declaration (I use it for entire Web App):

public class IdentityController : Controller
{

    private static EmailAdpater EmailAdapter = new EmailAdpater();
    private static UserIdentityTokenShortener TokenShortener = new UserIdentityTokenShortener();
    public static LoginTokenManager LoginTokenManager = new LoginTokenManager();
    ...

Has anyone encountered this problem? What did I do wrong?

EDIT: Added StackTrace and Details EDIT2: Edited title so future people can search this topic in case they have the same problem.

enter image description here

   at System.Collections.Generic.Dictionary12.Insert(TKey key, TValue value, Boolean add)
   at System.Collections.Generic.Dictionary12.Add(TKey key, TValue value)
   at MobileDatingAPI.Models.LoginTokenManager.GetUserIdFromToken(String token) in d:\FPT University\Capstone 2\TFSRepo\Projects\MobileDatingAPI\MobileDatingAPI\Models\LoginTokenManager.cs:line 48
   at MobileDatingAPI.Models.Utils.GetUserFromTokenID(Controller controller, String token, BaseApiViewModels model) in d:\FPT University\Capstone 2\TFSRepo\Projects\MobileDatingAPI\MobileDatingAPI\Models\Utils\Utils.cs:line 68
   at MobileDatingAPI.Controllers.CommunityController.UpdateActivity(String token, Nullable11 longitude, Nullable11 latitude) in d:\FPTUniversity\Capstone 2\TFSRepo\Projects\MobileDatingAPI\MobileDatingAPI\Controllers\CommunityController.cs:line 84
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary12 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary12 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult12.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
like image 367
Luke Vo Avatar asked Jan 08 '23 18:01

Luke Vo


1 Answers

I use Multi-Threading

This is almost certainly the problem then: since everything in your code is null-checked and/or created with values that are non-null, the only place where the problem could happen is the implementation of Dictionary.

When the capacity of the dictionary is reached, the collection re-allocates its internal data structures. If one thread catches the dictionary in the middle of reallocation performed by another thread, some of the variables inside the buckets would be uninitialized, i.e. null or default, depending on the type. Since this is not a usual state for a bucket, the dictionary will try to dereference it, causing null pointer exception.

In order to avoid this problem, add synchronization around the call of Add:

// Add this declaration where you declare TokenCache dictionary
object TokenCacheLock = new object();
...
lock (TokenCacheLock) {
    // Add a lock around your access of TokenCache
    if (this.TokenCache.ContainsKey(token)) ...
}

Note that since additions to the dictionary happen concurrently with reads, all accesses to TokenCache need to be synchronized.

A much simpler solution would be using ConcurrentDictionary<K,V>.

like image 161
Sergey Kalinichenko Avatar answered Jan 11 '23 22:01

Sergey Kalinichenko