Question

Could anyone create a short sample that breaks, unless the [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] is applied?

I just ran through this sample on MSDN and am unable to get it to break, even if I comment out the ReliabilityContract attribute. Finally seems to always get called.

Was it helpful?

Solution

using System;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;

class Program {
    static bool cerWorked;

    static void Main( string[] args ) {
        try {
            cerWorked = true;
            MyFn();
        }
        catch( OutOfMemoryException ) {
            Console.WriteLine( cerWorked );
        }
        Console.ReadLine();
    }

    unsafe struct Big {
        public fixed byte Bytes[int.MaxValue];
    }

    //results depends on the existance of this attribute
    [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] 
    unsafe static void StackOverflow() {
        Big big;
        big.Bytes[ int.MaxValue - 1 ] = 1;
    }

    static void MyFn() {
        RuntimeHelpers.PrepareConstrainedRegions();
        try {
            cerWorked = false;
        }
        finally {
            StackOverflow();
        }
    }
}

When MyFn is jitted, it tries to create a ConstrainedRegion from the finally block.

  • In the case without the ReliabilityContract, no proper ConstrainedRegion could be formed, so a regular code is emitted. The stack overflow exception is thrown on the call to Stackoverflow (after the try block is executed).

  • In the case with the ReliabilityContract, a ConstrainedRegion could be formed and the stack requirements of methods in the finally block could be lifted into MyFn. The stack overflow exception is now thrown on the call to MyFn (before the try block is ever executed).

OTHER TIPS

The primary driver for this functionality was to support SQL Servers stringent requirements for integrating the CLR into SQL Server 2005. Probably so that others could use and likely for legal reasons this deep integration was published as a hosting API but the technical requirements were SQL Servers. Remember that in SQL Server, MTBF is measured in months not hours and the process restarting because an unhandled exception happened is completely unacceptable.

This MSDN Magazine article is probably the best one that I've seen describing the technical requirements the constrained execution environment was built for.

The ReliabilityContract is used to decorate your methods to indicate how they operate in terms of potentially asynchronous exceptions (ThreadAbortException, OutOfMemoryException, StackOverflowException). A constrained execution region is defined as a catch or finally (or fault) section of a try block which is immediately preceded by a call to System.Runtime.CompilerServices.RuntimeServices.PrepareConstrainedRegions().

System.Runtime.CompilerServices.RuntimeServices.PrepareConstrainedRegions();
try 
{
    // this is not constrained
} 
catch (Exception e) 
{
    // this IS a CER
} 
finally 
{
    // this IS ALSO a CER
}

When a ReliabilityContract method is used from within a CER, there are 2 things that happen to it. The method will be pre-prepared by the JIT so that it won't invoke the JIT compiler the first time it's executed which could try to use memory itself and cause it's own exceptions. Also while inside of a CER the runtime promises not to throw a ThreadAbort exception and will wait to throw the exception until after the CER has completed.

So back to your question; I'm still trying to come up with a simple code sample that will directly answer your question. As you may have already guessed though, the simplest sample is going to require quite a lot of code given the asynchronous nature of the problem and will likely be SQLCLR code because that is the environment which will use CERs for the most benefit.

Are you running the MSDN sample under the debugger? I do not think it is possible for CER to function when you are executing within the debugger, as the debugger itself changes the nature of execution anyway.

If you build and run the app in optimized release mode, you should be able to see it fail.

While I don't have a concrete example for you, I think you're missing the point of have a try..finally block inside of the methods that guarantee success. The whole point of saying that the method will always succeed means that regards of what (exception) happens during execution, steps will be taken to ensure the data being accessed will be in a valid state when the method returns. Without the try..finally, you wouldn't be ensuring anything, and could mean that only half of the operations you wanted to happen, would happen. Thus, Cer.Success doesn't actually guarantee success, it only states that you as the developer are guaranteeing success.

Check out this page for an explanation of the differences between Success and MayFail states as it pertains to an Array.CopyTo method: http://weblogs.asp.net/justin_rogers/archive/2004/10/05/238275.aspx

CER attributes are means of documentation. They do influence how CLR will execute code in some situations, but I believe they (or lack of them) will never result in error in current versions of .NET.

They are mostly 'reserved for future use'.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top