3

We are running a very large web application in asp.net MVC .NET 4.0. Recently we had an audit done and the performance team says that there were a lot of null reference exceptions.

So I started investigating it from the dumps and event viewer. My understanding was as follows:

We are using Asyn Tasks in our controllers. We rely on HttpContext.Current.Items hashtable to store a lot of Application level values.

Task<Articles>.Factory.StartNew(() =>
        {
            System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context;
            var service = new ArticlesService(page);
            return service.GetArticles();
        }).ContinueWith(t => SetResult(t, "articles"));

So we are copying the context object onto the new thread that is spawned from Task factory. This context.Items is used again in the thread wherever necessary. Say for ex:

public class SomeClass
  {
    internal static int StreamID
    {
        get
        {
            if (HttpContext.Current != null)
            {
                return (int)HttpContext.Current.Items["StreamID"];
            }
            else
            {
                return DEFAULT_STREAM_ID;
            }
        }
    }

This runs fine as long as number of parallel requests are optimal. My questions are as follows:

1. When the load is more and there are too many parallel requests, I notice that HttpContext.Current.Items is empty. I am not able to figure out a reason for this and this causes all the null reference exceptions.

2. How do we make sure it is not null ? Any workaround if present ?

NOTE: I read through in StackOverflow and people have questions like HttpContext.Current is null - but in my case it is not null and its empty. I was reading one more article where the author says that sometimes request object is terminated and it may cause problems since dispose is already called on objects. I am doing a copy of Context object - its just a shallow copy and not a deep copy.

2 Answers2

2

Your problem is that a instance members of the HttpContext are not thread safe:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

When accessing it the way you are doing (multiple threads) you need to do your own synchronization.

static object locker = new object();
get
{
    lock (locker)
    {
        if (HttpContext.Current != null)
        {
            return (int)HttpContext.Current.Items["StreamID"];
        }
        else
        {
            return DEFAULT_STREAM_ID;
        }
    }
}

MSDN: system.web.httpcontext

Peter
  • 27,590
  • 8
  • 64
  • 84
  • I am not concerned about the thread safety here. Even if I read and write multiple times by many threads I am OK with it. But what I wanted to know is will adding this lock prevent the HttpContext.Current.Items from becoming empty ? – Gurucharan Balakuntla Maheshku Aug 19 '14 at 03:44
  • 2
    I like you comment, you are asking what the problem is in your code. When someone points out what the problem is, you say that you do not care. The point is that the collection is not thread safe, that means that you will get unexpected behavior, an empty collection is just one thing. The behavior is just undefined. You need to implement the lock in all places where your access the hashtable. – Peter Aug 22 '14 at 09:43
  • Thanks Peer - I modified the code as per your instruction. Now I see the number of exceptions has come down from 10000 in 1 day to around 4000 per day. The problem is not fully resolved; but I am happy with the improved performance. – Gurucharan Balakuntla Maheshku Aug 25 '14 at 01:57
  • @GuruC: I recommend avoiding use of `HttpContext.Current.Items` in asynchronous code. Not because it isn't thread safe but because it gets cleared in the guts of `Response.End()` and `Response.Abort()`. I was bitten by this in Oneway web service methods because IIS returns `HTTP/202 Accepted` to callers before it passes control to your web methods ... then you have a race condition retrieving your data before IIS clears it out. Perhaps you're facing a similar issue with `async` methods? – AlwaysLearning Aug 24 '17 at 04:59
0

Maybe I'm misreading this, but I'm getting the impression that you're only trying to prevent the null reference error.

public class SomeClass
{
    internal static int StreamID
    {
        get
        {
            int returnValue;
            if (HttpContext.Current != null)
            {
                if(HttpContext.Current.Items["StreamID"] != null)
                {
                    returnValue = (int)HttpContext.Current.Items["StreamID"];
                }
                else
                {
                    returnValue = DEFAULT_STREAM_ID;
                }  
            }
            else
            {
                returnValue = DEFAULT_STREAM_ID;
            }
            return returnValue;
        }
    }
}
JimmyBytes
  • 57
  • 5