LockRecursionException thrown from various locations
See original GitHub issueDescription
We’ve just had a report from an Evoq customer who have a busy site and observe intermittent downtime caused by “thread-safe” code. Quick google finds a whole lot of similar reports dating back to 2011. Let’s put this one to bed once and for all.
https://www.dnnsharp.com/helpcenter/url-adapter/recursive-read-lock-acquisitions-not-allowed-in-this-mode http://www.dnnsoftware.com/forums/forumid/108/threadid/412632/scope/posts http://www.dnnsoftware.com/answers/help-with-error-recursive-read-lock-acquisitions-not-allowed-in-this-mode https://dnntracker.atlassian.net/browse/DNN-24111
Looking at the most recent report we received, out of 57k exceptions logged (40 per minute!) there are two prime sources which fail to acquire a lock:
DotNetNuke.UI.Containers.Container.InvokeContainerEvents
(22k)DotNetNuke.ComponentModel.SimpleContainer.GetComponentType
(34k)
InvokeContainerEvents
The exception is logged in various places in the UI lifecycle. For example
DotNetNuke.Services.Exceptions.Exceptions - System.Threading.LockRecursionException: Recursive read lock acquisitions not allowed in this mode.
at System.Threading.ReaderWriterLockSlim.TryEnterReadLockCore(TimeoutTracker timeout)
at System.Threading.ReaderWriterLockSlim.TryEnterReadLock(TimeoutTracker timeout)
at DotNetNuke.Collections.Internal.ReaderWriterLockStrategy.GetReadLock(TimeSpan timeout)
at DotNetNuke.UI.Containers.Container.InvokeContainerEvents(ContainerEventType containerEventType)
at System.Web.UI.Control.InitRecursive(Control namingContainer)
at System.Web.UI.Control.AddedControl(Control control, Int32 index)
at DotNetNuke.UI.Skins.Pane.InjectModule(ModuleInfo module)
@mean2me and I tried to look for more usages of the ContainerEventListener
and I could not find any. This extension point used is apparently not used at all by Platform itself or Evoq.
What I find weird is that this code actually acquires a lock. To only iterate over the listeners it should perfectly fine to simply get hold of the underlying SharedList#BackingList
to get a non-thread safe collection instead.
SimpleContainer
Often when trying to access an element from the container the lock cannot be acquired. Here’s an example
DotNetNuke.Web.Common.Internal.DotNetNukeHttpApplication - System.Threading.LockRecursionException: Recursive read lock acquisitions not allowed in this mode.
at System.Threading.ReaderWriterLockSlim.TryEnterReadLockCore(TimeoutTracker timeout)
at System.Threading.ReaderWriterLockSlim.TryEnterReadLock(TimeoutTracker timeout)
at DotNetNuke.Collections.Internal.ReaderWriterLockStrategy.GetReadLock(TimeSpan timeout)
at DotNetNuke.ComponentModel.SimpleContainer.GetComponentType(Type contractType)
at DotNetNuke.ComponentModel.SimpleContainer.GetComponent(Type contractType)
at DotNetNuke.ComponentModel.AbstractContainer.GetComponent[TContract]()
at DotNetNuke.ComponentModel.ComponentBase`2.get_Instance()
at DotNetNuke.Entities.Urls.RewriterUtils.OmitFromRewriteProcessing(String localPath)
at DotNetNuke.Common.Initialize.ProcessHttpModule(HttpRequest request, Boolean allowUnknownExtensions, Boolean checkOmitFromRewriteProcessing)
at DotNetNuke.HttpModules.RequestFilter.RequestFilterModule.FilterRequest(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Discussion
Seems like the underlying problem for both problems is the SharedList
. One possibility is that locks are not properly released and then the application fails to acquire a new one. Unfortunately it’s very hard to verify if that is true. While it will be impractical to replace or rewrite that class we could safely reduce the locks where they are not strictly necessary.
In the case of InvokeContainerEvents
I think that there is no risk in skipping lock altogether. The consequence would be a possibility that certain executions do not get the latest state of the list in question. And so what? This code in Container.cs
does not modify the collection at all and is executed at every request. The latter should also mean the removing the lock should improve performance.
As for the SimpleContainer
I think the fix should be more decisive. It’s a mystery to me why this service locator pattern and homegrown “dependency injection container”. Maybe it is high time to replace with a well-established alternative? Some full-featured options offering good performance are DryIoC, LightInject or even Autofac.
Affected version
Probably all the way back to DNN 5. Maybe even before that but we don’t have the evidence.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:9 (9 by maintainers)
Top GitHub Comments
I’d vote for Autofac to replace
SimpleContainer
@daguiler, done.