Constrained Execution Regions
A constrained execution region (CER) is part of a mechanism for authoring reliable managed code. A CER defines an area in which the common language runtime (CLR) is constrained from throwing out-of-band exceptions that would prevent the code in the area from executing in its entirety. Within that region, user code is constrained from executing code that would result in the throwing of out-of-band exceptions. The PrepareConstrainedRegions method must immediately precede a
try block and marks
fault blocks as constrained execution regions. Once marked as a constrained region, code must only call other code with strong reliability contracts, and code should not allocate or make virtual calls to unprepared or unreliable methods unless the code is prepared to handle failures. The CLR delays thread aborts for code that is executing in a CER.
CER is only supported in .NET Framework. This article doesn't apply to .NET Core or .NET 5 and above.
Constrained execution regions are used in different forms in the CLR in addition to an annotated
try block, notably critical finalizers executing in classes derived from the CriticalFinalizerObject class and code executed using the ExecuteCodeWithGuaranteedCleanup method.
CER Advance Preparation
The CLR prepares CERs in advance to avoid out-of-memory conditions. Advance preparation is required so the CLR does not cause an out of memory condition during just-in-time compilation or type loading.
The developer is required to indicate that a code region is a CER:
The top level CER region and methods in the full call graph that have the ReliabilityContractAttribute attribute applied are prepared in advance. The ReliabilityContractAttribute can only state guarantees of Success or MayFail.
Advance preparation cannot be performed for calls that cannot be statically determined, such as virtual dispatch. Use the PrepareMethod method in these cases. When using the ExecuteCodeWithGuaranteedCleanup method, the PrePrepareMethodAttribute attribute should be applied to the clean up code.
Users are constrained in the type of code they can write in a CER. The code cannot cause an out-of-band exception, such as might result from the following operations:
Acquiring a lock.
Calling unprepared methods virtually.
Calling methods with a weak or nonexistent reliability contract.
In the .NET Framework version 2.0, these constraints are guidelines. Diagnostics are provided through code analysis tools.
The ReliabilityContractAttribute is a custom attribute that documents the reliability guarantees and the corruption state of a given method.
Reliability guarantees, represented by Cer enumeration values, indicate the degree of reliability of a given method:
MayFail. Under exceptional conditions, the method might fail. In this case, the method reports back to the calling method whether it succeeded or failed. The method must be contained in a CER to ensure that it can report the return value.
None. The method, type, or assembly has no concept of a CER and is most likely not safe to call within a CER without substantial mitigation from state corruption. It does not take advantage of CER guarantees. This implies the following:
Under exceptional conditions the method might fail.
The method might or might not report that it failed.
The method is not written to use a CER, the most likely scenario.
If a method, type, or assembly is not explicitly identified to succeed, it is implicitly identified as None.
Success. Under exceptional conditions, the method is guaranteed to succeed. To achieve this level of reliability you should always construct a CER around the method that is called, even when it is called from within a non-CER region. A method is successful if it accomplishes what is intended, although success can be viewed subjectively. For example, marking Count with
ReliabilityContractAttribute(Cer.Success)implies that when it is run under a CER, it always returns a count of the number of elements in the ArrayList and it can never leave the internal fields in an undetermined state. However, the CompareExchange method is marked as success as well, with the understanding that success may mean the value could not be replaced with a new value due to a race condition. The key point is that the method behaves in the way it is documented to behave, and CER code does not need to be written to expect any unusual behavior beyond what correct but unreliable code would look like.
Corruption levels, represented by Consistency enumeration values, indicate how much state may be corrupted in a given environment:
MayCorruptAppDomain. Under exceptional conditions, the common language runtime (CLR) makes no guarantees regarding state consistency in the current application domain.
MayCorruptInstance. Under exceptional conditions, the method is guaranteed to limit state corruption to the current instance.
MayCorruptProcess, Under exceptional conditions, the CLR makes no guarantees regarding state consistency; that is, the condition might corrupt the process.
WillNotCorruptState. Under exceptional conditions, the method is guaranteed not to corrupt state.
try/catch/finally is an exception handling mechanism with the same level of predictability guarantees as the unmanaged version. The
catch/finally block is the CER. Methods in the block require advance preparation and must be noninterruptible.
In the .NET Framework version 2.0, code informs the runtime that a try is reliable by calling PrepareConstrainedRegions immediately preceding a try block. PrepareConstrainedRegions is a member of RuntimeHelpers, a compiler support class. Call PrepareConstrainedRegions directly pending its availability through compilers.
A noninterruptible region groups a set of instructions into a CER.
In .NET Framework version 2.0, pending availability through compiler support, user code creates non-interruptible regions with a reliable try/catch/finally that contains an empty try/catch block preceded by a PrepareConstrainedRegions method call.
Critical Finalizer Object
A CriticalFinalizerObject guarantees that garbage collection will execute the finalizer. Upon allocation, the finalizer and its call graph are prepared in advance. The finalizer method executes in a CER, and must obey all the constraints on CERs and finalizers.
Any types inheriting from SafeHandle and CriticalHandle are guaranteed to have their finalizer execute within a CER. Implement ReleaseHandle in SafeHandle derived classes to execute any code that is required to free the handle.
Code Not Permitted in CERs
The following operations are not permitted in CERs:
Acquiring a lock.
Multidimensional array access.
Method calls through reflection.
Security checks. Do not perform demands, only link demands.
Getting or setting fields on a transparent proxy.
Function pointers and delegates.