DelegateReference unit tests fail in "Release" mode

Aug 29, 2008 at 1:09 AM
I was preparing to do a build of CAL today and thought I'd run the unit tests before committing the .dll to TFS.  I had already switched to Release.  To my surprise, one of the unit tests failed, "NotKeepAliveKeepsDelegateIfStillAlive" (in DelegateReferenceFixture.cs).  After some digging the only conclusion I can come to is that in Release mode either the C# compiler or the JIT compiler do some optimization that causes the unit test to fail (even though I'm quite certain the WeakReference object being used by DelegateReference works as it should).

        [TestMethod]
        public void NotKeepAliveKeepsDelegateIfStillAlive()
        {
            var delegates = new SomeClassHandler();
            var delegateReference = new DelegateReference((Action<string>) delegates.DoEvent, false);

            GC.Collect();

            Assert.IsNotNull(delegateReference.Target);  // This Assert fails in "Release"

            delegates = null;
            GC.Collect();

            Assert.IsNull(delegateReference.Target);
        }

Out of curiousity, can anyone explain why?
Aug 29, 2008 at 7:53 PM
Hi Jeremy,
Thanks for the heads up. We ran into a similar thing while we were doing perfomance testing on CompositeWpfEvent. As you said, the issue is not in DelegateReference, but in the test.
The compiler makes some optimizations because delegates variable is not being used anymore, so it removes the reference immediately.
If you for example add the following line, the test will work correctly:

            Assert.IsNotNull(delegateReference.Target);
            Assert.IsNotNull(delegates);    //this uses delegates in order to avoid compiler optimizations
            delegates = null;

Thanks, and I'll make sure we fix this test for next release,
Julian Dominguez
http://blogs.southworks.net/jdominguez
Developer
Aug 29, 2008 at 7:59 PM
Yes this is definitely due to optimziation.  You could also do:


    Assert.IsNotNull(delegateReference.Target);
    GC.KeepAlive(delegates);
    delegates = null;

Which will keep the delegates alive until that point.  I like this better as it may help explain it in-code a bit better.

-b