📜 ⬆️ ⬇️

Interesting task: we increase the stability (robustness) of applications (part 3) - the code for CER and other feints with ears

In continuation of the topic of sustainable applications and CER (start here ) - we will consider how to prepare an arbitrary (or almost arbitrary) piece of code for execution in the Constrained Execution region.

Why is this necessary? Remember, I said that in the process of preparing for CER there is an inspection of the stack and pre-jitt. But to inspect the stack, if the call is made through the interface or a virtual method is impossible in advance. Therefore, this code:
...     RuntimeHelper.PrepareConstrainedRegions();      try {}     finally {          obj.SomeVirtualMethod();     } 
compiles, but at run-time does not reach the desired.

In order to prepare virtual methods, you need to use another feature of the RuntimeHelpers class - the PrepareMethod () method:
     ...    RuntimeHelpers.PrepareMethod(obj.GetType().GetMethod("SomeVirtualMethod").MethodHandle);    RuntimeHelpers.PrepareConstrainedRegions();     try {}    finally {         obj.SomeVirtualMethod();    } 
Now the region will be prepared and will work normally.
I think you already understood that in the case of a call through an interface, you should prepare methods of all classes that can be hidden behind the interface. Sad but true.
For delegates, the procedure is similar, but remember that PrepareDelegate works only for the first delegate in the list — this means that only the first delegate in the list will be processed in MulticastDelegate — you will need to manually go through all the delegates from the list and prepare them.
If you use delegates only for events, causing events from CER, the situation is somewhat simpler - using the code below allows you to automatically prepare delegates:
 public event EventHandler MyEvent {  add {    if (value == null) return;    RuntimeHelpers.PrepareDelegate(value);    lock(this) _myEvent += value;  }   remove { lock(this) _myEvent -= value; } } 
It is clear that with great force comes a great responsibility. And the ability to execute any code without limiting and throwing asynchronous exceptions is a big power. The .net infrastructure allows the developer to specify what kind of responsibility the code takes on while running under CER. And he does this with the help of the System.Runtime.ConstrainedExecution.ReliabilityContractAttribute attribute. This attribute can be applied both at the assembly or class level, and at the method level, providing the necessary level of detail.
If you try to apply this attribute, you will see that it can set two properties from the valid enumerations: Cer and ConsistencyGuarantee. ConsistencyGuarantee is responsible for what kind of “destruction” a method can inflict if you score on asynchronous exceptions when performing it under Cer. His ad looks like this (I don’t need to translate here, I think):
 public enum Consistency { MayCorruptProcess = 0, MayCorruptAppDomain = 1, MayCorruptInstance = 2, WillNotCorruptState = 3 } 
And Cer is responsible for how successful the method call will be. It can take values
 { None = 0, MayFail = 1, Success = 2 } 

Despite the large number of combinations of these properties, for the methods with which the execution of CER begins, only 3 combinations are valid:
- [ReliabilityContract (Consistency.MayCorruptInstance, Cer.MayFail)] - “my hut is on the edge, I know nothing”; if an error occurs, the object instance will not suffer anything.
- [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - “clean” fall: even if you fell, you can continue to work;
[ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - “the method will definitely not fall”
By default, all methods are considered to have the [ReliabilityContract (Consistency.WillCorruptProcess, Cer.None) attribute.
These execution contracts will be interpreted by the framework's hosting environment in order to respond to the problem accordingly - for example, “nail” the domain in advance if something fatal has already happened, not allowing the program to damage anything else.

Finally, here's what. acerv asked about StackOverflow in the last post. "- They asked? We answer!" - .net framework does not guarantee that the thread will execute any finally block in the case of a StackOverflowException. Nevertheless, there is not very beautiful, but a simple opportunity to execute some clean-up code guaranteed - all with the help of the same RuntimeHelpers. I will not explain much:
 try {    RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup( TryMethod, CleanupMethod, this); } catch(SomeException exc) { ... } // Handle unexpected failure ... [MethodImpl(MethodImplOptions.NoInlining)] void TryMethod(object userdata) { ... } [MethodImpl(MethodImplOptions.NoInlining)] [ReliabilityContract(Cer.Success, Consistency.MayCorruptInstance)] void CleanupMethod(object userdata, bool fExceptionThrown) { ... } 

')
finally

I hope it was interesting.
However, the theme was long! :) And there is something to say about SafeHandles, MemoryBarries and FailFast. Wait :)

Source: https://habr.com/ru/post/70085/


All Articles