Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Unity be made to not throw SynchronizationLockException all the time?

The Unity dependency injection container has what seems to be a widely known issue where the SynchronizedLifetimeManager will often cause the Monitor.Exit method to throw a SynchronizationLockException which is then caught and ignored. This is a problem for me because I like to debug with Visual Studio set to break on any thrown exception, so every time my application starts up I'm breaking on this exception multiple times for no reason.

How can I prevent this exception from being thrown?

Wherever this issues is mentioned elsewhere on the web, the advice usually involves changing the debugger settings to ignore it. This is akin to going to the doctor and saying, "Doctor, Doctor, my arm hurts when I raise it," to be told, "Well, stop raising it." I'm looking for a solution that stops the exception being thrown in the first place.

The exception occurs in the SetValue method because it makes the assumption that GetValue will have been called first, where Monitor.Enter is called. However, the LifetimeStrategy and UnityDefaultBehaviorExtension classes both regularly call SetValue without calling GetValue.

I'd rather not have to change the source code and maintain my own version of Unity, so I'm hoping for a solution where I can add some combination of extensions, policies, or strategies to the container that will ensure that, if the lifetime manager is a SynchronizedLifetimeManager, GetValue is always called before anything else.

like image 955
Rory MacLeod Avatar asked May 20 '10 12:05

Rory MacLeod


5 Answers

I'm sure there's a lot of ways code could call SynchronizedLifetimeManager, or a descendant like ContainerControlledLifetimeManager, but there were two scenarios in particular that were causing me problems.

The first was my own fault - I was using constructor injection to supply a reference to the container, and in that constructor I was also adding the new instance of the class to the container for future use. This backwards approach had the effect of changing the lifetime manager from Transient to ContainerControlled so that the object Unity called GetValue on was not the same object it called SetValue on. The lesson learned is don't do anything during build-up that could change an object's lifetime manager.

The second scenario was that every time RegisterInstance is called, UnityDefaultBehaviorExtension calls SetValue without calling GetValue first. Luckily, Unity is extensible enough that, with enough bloody-mindedness, you can work around the problem.

Start with a new behavior extension like this:

/// <summary>
/// Replaces <see cref="UnityDefaultBehaviorExtension"/> to eliminate 
/// <see cref="SynchronizationLockException"/> exceptions that would otherwise occur
/// when using <c>RegisterInstance</c>.
/// </summary>
public class UnitySafeBehaviorExtension : UnityDefaultBehaviorExtension
{
    /// <summary>
    /// Adds this extension's behavior to the container.
    /// </summary>
    protected override void Initialize()
    {
        Context.RegisteringInstance += PreRegisteringInstance;

        base.Initialize();
    }

    /// <summary>
    /// Handles the <see cref="ExtensionContext.RegisteringInstance"/> event by
    /// ensuring that, if the lifetime manager is a 
    /// <see cref="SynchronizedLifetimeManager"/> that its 
    /// <see cref="SynchronizedLifetimeManager.GetValue"/> method has been called.
    /// </summary>
    /// <param name="sender">The object responsible for raising the event.</param>
    /// <param name="e">A <see cref="RegisterInstanceEventArgs"/> containing the
    /// event's data.</param>
    private void PreRegisteringInstance(object sender, RegisterInstanceEventArgs e)
    {
        if (e.LifetimeManager is SynchronizedLifetimeManager)
        {
            e.LifetimeManager.GetValue();
        }
    }
}

Then you need a way to replace the default behavior. Unity doesn't have a method to remove a specific extension, so you have to remove everything and put the other extensions back in again:

public static IUnityContainer InstallCoreExtensions(this IUnityContainer container)
{
    container.RemoveAllExtensions();
    container.AddExtension(new UnityClearBuildPlanStrategies());
    container.AddExtension(new UnitySafeBehaviorExtension());

#pragma warning disable 612,618 // Marked as obsolete, but Unity still uses it internally.
    container.AddExtension(new InjectedMembers());
#pragma warning restore 612,618

    container.AddExtension(new UnityDefaultStrategiesExtension());

    return container;
}

Notice that UnityClearBuildPlanStrategies? RemoveAllExtensions clears out all of the container's internal lists of policies and strategies except for one, so I had to use another extension to avoid inserting duplicates when I restored the default extensions:

/// <summary>
/// Implements a <see cref="UnityContainerExtension"/> that clears the list of 
/// build plan strategies held by the container.
/// </summary>
public class UnityClearBuildPlanStrategies : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.BuildPlanStrategies.Clear();
    }
}

Now you can safely use RegisterInstance without fear of being driven to the brink of madness. Just to be sure, here's some tests:

[TestClass]
public class UnitySafeBehaviorExtensionTests : ITest
{
    private IUnityContainer Container;
    private List<Exception> FirstChanceExceptions;

    [TestInitialize]
    public void TestInitialize()
    {
        Container = new UnityContainer();
        FirstChanceExceptions = new List<Exception>();
        AppDomain.CurrentDomain.FirstChanceException += FirstChanceExceptionRaised;
    }

    [TestCleanup]
    public void TestCleanup()
    {
        AppDomain.CurrentDomain.FirstChanceException -= FirstChanceExceptionRaised;
    }

    private void FirstChanceExceptionRaised(object sender, FirstChanceExceptionEventArgs e)
    {
        FirstChanceExceptions.Add(e.Exception);
    }

    /// <summary>
    /// Tests that the default behavior of <c>UnityContainer</c> leads to a <c>SynchronizationLockException</c>
    /// being throw on <c>RegisterInstance</c>.
    /// </summary>
    [TestMethod]
    public void UnityDefaultBehaviorRaisesExceptionOnRegisterInstance()
    {
        Container.RegisterInstance<ITest>(this);

        Assert.AreEqual(1, FirstChanceExceptions.Count);
        Assert.IsInstanceOfType(FirstChanceExceptions[0], typeof(SynchronizationLockException));
    }

    /// <summary>
    /// Tests that <c>UnitySafeBehaviorExtension</c> protects against <c>SynchronizationLockException</c>s being
    /// thrown during calls to <c>RegisterInstance</c>.
    /// </summary>
    [TestMethod]
    public void SafeBehaviorPreventsExceptionOnRegisterInstance()
    {
        Container.RemoveAllExtensions();
        Container.AddExtension(new UnitySafeBehaviorExtension());
        Container.AddExtension(new InjectedMembers());
        Container.AddExtension(new UnityDefaultStrategiesExtension());

        Container.RegisterInstance<ITest>(this);

        Assert.AreEqual(0, FirstChanceExceptions.Count);
    }
}

public interface ITest { }
like image 79
Rory MacLeod Avatar answered Nov 12 '22 23:11

Rory MacLeod


Fixed in the latest release of Unity (2.1.505.2). Get it via NuGet.

like image 20
Grigori Melnik Avatar answered Nov 12 '22 23:11

Grigori Melnik


The answer to your question is unfortunately no. I followed up on this with the dev team here at the Microsoft patterns & practices group (I was the dev lead there until recently)and we had this as a bug to consider for EntLib 5.0. We did some investigation and came to the conclusion that this was caused by some unexpected interactions between our code and the debugger. We did consider a fix but this turned out to be more complex than the existing code. In the end this got prioritized below other things and didn't make the bar for 5.

Sorry I don't have a better answer for you. If it's any consolation I find it irritating too.

like image 10
Ade Miller Avatar answered Nov 13 '22 00:11

Ade Miller


I use this short solution:

/// <summary>
/// KVV 20110502
/// Fix for bug in Unity throwing a synchronizedlockexception at each register
/// </summary>
class LifeTimeManager : ContainerControlledLifetimeManager
{
    protected override void SynchronizedSetValue(object newValue)
    {
        base.SynchronizedGetValue();
        base.SynchronizedSetValue(newValue);
    }
}

and use it like this:

private UnityContainer _container;
...
_container.RegisterInstance(instance, new LifeTimeManager());

the problem is that the base class of ContainerControlledLifetimeManager expects a SynchronizedSetValue to do a monitor.Enter() via the base.GetValue, however the ContainerControlledLifetimeManager class fails to do this (apparently its developers didn't have 'break at exception' enabled?).

regards, Koen

like image 7
Koen VV Avatar answered Nov 12 '22 23:11

Koen VV


Rory's solution is great - thanks. Solved an issue that annoys me every day! I made some minor tweaks to Rory's solution so that it handles whatever extensions are registered (in my case i had a WPF Prism/Composite extension)..

    public static void ReplaceBehaviourExtensionsWithSafeExtension(IUnityContainer container)
    {
        var extensionsField = container.GetType().GetField("extensions", BindingFlags.Instance | BindingFlags.NonPublic);
        var extensionsList = (List<UnityContainerExtension>)extensionsField.GetValue(container);
        var existingExtensions = extensionsList.ToArray();
        container.RemoveAllExtensions();
        container.AddExtension(new UnitySafeBehaviorExtension());
        foreach (var extension in existingExtensions)
        {
            if (!(extension is UnityDefaultBehaviorExtension))
            {
                container.AddExtension(extension);
            }
        }
    }
like image 4
Zubin Appoo Avatar answered Nov 13 '22 00:11

Zubin Appoo