📜 ⬆️ ⬇️

Interesting task: we increase the stability (robustness) of applications (part 2)

So, I bring a solution to the problem from the topic of habrahabr.ru/blogs/net/69545 - about the guaranteed release of the unmanaged resource.

As comrade adontz correctly noted, the problem is solved with the help of CER.
CER is a technology of Constrained Execution Region, with the help of which you can secure your code by guaranteeing that in some part of it _ _ _ guaranteed asynchronous interrupts like StackOverflow or ThreadAbort, or jitter has fallen off - anything can happen, and it’s not always possible to leave external resources in a consistent state.

Togo!


In other words, we can “force” the runtime by blocking a part of the code and preparing everything for its execution: check that we have enough memory on the stack and in the RAM, for-jitt code, and then - again! - execute the prepared code, and then figure out if anything has happened.
')
So that the execution of the code does not “fall off” in the process, runtime imposes some restrictions on what _not_ in the CER region. And, in general, you just can't do a lot of things like this:In general, under CER one should “drive” a small critical piece of code (as a rule, work with external resources), the continuous execution of which should be guaranteed. Just what we need.

So back to our sheep hard weekdays, i.e. let's move on to the examples. To use CER in practice, you need to dig in the direction of the class helper System.Runtime.CompilerServices.RuntimeHelpers .
His most important method for us is PrepareConstrainedRegions , and performs the described preparation — for example, passes along the _the future_ call stack, performing pre-jitt and placing stop-the-world marks (it’s now clear why you can’t use virtual methods and interfaces there is dynamic scheduling, and it is impossible to make a call stack until the actual execution of the code). Now the fun part. Using this method should look like this:
 ... System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); try {} finally {    // atomic-    } ... 
That's right - an empty try block, and all uninterrupted operations should go in a finally block!

To test this test:
 Thread t = new Thread(new ThreadStart(        () => {        RuntimeHelpers.PrepareConstrainedRegions();        try { }        finally {            while (true) { }    }}));    t.Start();    Thread.Sleep(1000);    t.Abort();    t.Join(); 

The program works and does not end. If you now comment out try-finally, the program will end. Quite - a request for a ThreadAbortException _not_ will be postponed until the end of the exit from the finally block (which does not happen because of the infinite while (true) {} loop), and will work right away (almost immediately)

Now we know how to “fix” our code from the example with IntPtr:
 IntPtr ptr = IntPtr.Zero; try {    //...    System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();    try { }    finally {        ptr = Marshal.AllocHGlobal(500);    }     // .., .. } finally {    if( ptr != IntPtr.Zero ) Marshal.FreeHGlobal(ptr); } ... 

For the future - MSDN advises to avoid using unmanaged resources. Somewhat strange advice in the light of the fact that one of the goals of the CLR is to interact with legacy unmanaged code and resources.

So I advise you to carefully look at workplaces with uncontrollable resources - practice shows that even in a fully working program at these points you can find 2-3 serious bugs.

It is clear that I only made out what helped me deal with the memory leak; but in CER itself I did not touch on the following: contracts for methods suitable for CER, preparation of events for calling inside CER, preparation of virtual methods, using Critical Finalizers, FailFast and MemoryBarriers for reliable program operation and preventing leaks of unmanaged resources. Do I need to write about it or send it in MSDN?

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


All Articles