I am seeing a pretty crazy error crop up when using the MVC MiniProfiler. Intermittently, the site I'm working on enters a state where every request results in this exception being thrown:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.TypeInitializationException: The type initializer for 'MvcMiniProfiler.MiniProfiler' threw an exception.
---> System.Threading.LockRecursionException: Write lock may not be acquired with read lock held.
This pattern is prone to deadlocks. Please ensure that read locks are released before taking a write lock.
If an upgrade is necessary, use an upgrade lock in place of the read lock.
at System.Threading.ReaderWriterLockSlim.TryEnterWriteLockCore(Int32 millisecondsTimeout)
at System.Threading.ReaderWriterLockSlim.TryEnterWriteLock(Int32 millisecondsTimeout)
at System.Web.Routing.RouteCollection.GetWriteLock()
at MvcMiniProfiler.UI.MiniProfilerHandler.RegisterRoutes()
in C:\Users\sam\Desktop\mvc-mini-profiler\MvcMiniProfiler\UI\MiniProfilerHandler.cs:line 81
at MvcMiniProfiler.MiniProfiler..cctor()
in C:\Users\sam\Desktop\mvc-mini-profiler\MvcMiniProfiler\MiniProfiler.cs:line 241
— End of inner exception stack trace —
at MvcMiniProfiler.MiniProfiler.get_Current()
at TotallyNotOverDrive.Boom.MvcApplication.Application_EndRequest()
The error persists until the app pool is recycled. Looks like somehow a lock is being held which prevents the MiniProfiler from trying to register it's routes. This occurs for requests where I am not starting the MiniProfiler, but during Application_EndRequest
I call MiniProfiler.Stop()
, which seems to result in a MiniProfiler being created when the Current property is accessed. For a simple solution, I modified EndRequest to use the same logic for stopping the profiler as BeginRequest, so if the request is not using the profiler this error should be avoided completely. I would still like to resolve the actual problem before sending this code to production.
My route table is pretty simple, and is only added to within the Application_Start
method. We are not using any other third-party code which might be modifying the route table after startup. The only suspect thing I've done with routing is add a custom Route to the table, but it's a pretty straightforward route, I just needed some more complicated pattern matching than a standard MVC route could accomplish.
I looked through the relevant MiniProfiler code and don't see anything that could be causing a lock to go unreleased, so I'm assuming it's a combination of ASP.NET and MiniProfiler conflicting when accessing the RouteTable. I can't reliably reproduce the problem, so I'm wondering if anyone else has had problems like this with routing. Thanks for any help you can offer.
I think I see what is happening, you need to get the routes registered earlier. You are registering them during EndRequest
, at this point there may be other requests in the pipeline which are holding read locks on the route table.
The routes are registered in the static constructor of MiniProfiler
. If you access any of the settings in MiniProfiler during Application_Start
, this will kick off the static constructor that will register the routes.
For example, try adding something like this, just after registering your routes.
MiniProfiler.Settings.PopupMaxTracesToShow = 10;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With