user1613512
user1613512

Reputation: 3640

Is Controller.HttpContext (HttpContextBase) thread safe?

If you have the following code in a (non Core) ASP.NET MVC 5 project:

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace ASPApp.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public Task<ActionResult> UnSafeThreadAccess()
        {
            PrintThreadId("UnSafeThreadAccess entry");
            var synchronisationContext = SynchronizationContext.Current;
            return Task.Delay(2).ContinueWith(_ =>
            {
                PrintThreadId("UnSafeThreadAccess continueWith");
                var controllerContext = HttpContext.Request.QueryString["q"];
                var content = $"Responding to {System.Web.HttpContext.Current.Request.QueryString["q"]}";
                return Content(content) as ActionResult;
            });
        } 

        private static void PrintThreadId(string threadName)
        {
            Trace.WriteLine($"{threadName}: {Thread.CurrentThread.ManagedThreadId}");
        }
    }
}

I knew that accessing System.Web.HttpContext.Current inside the ContinueWith is bad, since it is bound to the entry thread of the controller. The point of this code is to illustrate why awaiting the Task.Delay is necessary in this example.

However I was a bit surprised that the HttpContext property of the controller can be accessed inside ContinueWith. How is this possible? I know the type of the property is not HttpContext but HttpContextBase, however I was expecting that this would just call through to the static HttpContext instance.

Upvotes: 0

Views: 803

Answers (1)

Adam Simon
Adam Simon

Reputation: 2970

However I was a bit surprised that the HttpContext property of the controller can be accessed inside ContinueWith. How is this possible? [...], however I was expecting that this would just call through to the static HttpContext instance.

Digging in the sources reveals that Controller.HttpContext is not a plain call-through to HttpContext.Current but HTTP context is retrieved from the request context ( System.Web.Routing.RequestContext) which actually stores a reference to the current HttpContextBase instance:

https://github.com/aspnet/AspNetWebStack/blob/v3.2.6/src/System.Web.Mvc/Controller.cs#L87
https://github.com/aspnet/AspNetWebStack/blob/v3.2.6/src/System.Web.Mvc/ControllerContext.cs#L71
https://github.com/Microsoft/referencesource/blob/4.6.2/System.Web/Routing/RequestContext.cs#L23

In this sense it's safe to access Controller.HttpContext from another thread than the entry thread of the request.

However, MSDN says the following about HttpContext:

This object is ready for garbage collection when the HttpRequest is completed. Its usage after the request completes could lead to undefined behavior, such as a NullReferenceException.

This object is only available in the thread controlled by ASP.NET. Usage in background threads could lead to undefined behavior.

This is not the most precise piece of phrasing but I assume that at least operations that don't mutate the state of the object (like reading the request query string) must be safe in background threads. (Before the request completes, of course.)

Upvotes: 1

Related Questions