
An interesting problem: we increase the stability (robustness) of applications (part 2)
So, I bring a solution to the problem from the topic habrahabr.ru/blogs/net/69545 - about the guaranteed release of an unmanaged resource.
As comrade adontz correctly noted , the problem is solved using CER.
CER is the Constrained Execution Region technology with which you can secure your code by guaranteeing that in some part of it asynchronous interruptions of the StackOverflow or ThreadAbort type are guaranteed to happen, or the jitter has “fallen off” - anything happens, and it’s not always possible to leave external resources in a consistent state.
In other words, we can “force” the runtime to block the code section and prepare everything for its execution: check that we have enough memory on the stack and in the RAM, j-code the code, and then break it! - execute the prepared code, and then figure out if something happened.
To prevent code execution from “falling off” in the process, runtime imposes some restrictions on what _not_ can_ be done in the CER region. And, in general, just so you can’t do a lot of things:
So, back to ourrams on a heavy weekday, i.e. let's move on to the examples. To use CER in practice, you need to dig towards the helper class
His most important method for us is
To check - this test:
The program works and does not end. If you now comment out try-finally, the program will end. Absolutely - a request for ThreadAbortException _not_ will be delayed until the end of the finally block (which does not happen due to the endless while (true) {} loop), but will work right away (almost immediately)
Now we know how to "fix" our code from the example with IntPtr:
For the future - MSDN recommends avoiding unmanaged resources. Somewhat strange advice in light of the fact that one of the goals of creating the CLR is to interact with legacy unmanaged code and resources.
So I advise you to carefully look at places of work with unmanaged 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 sorted out only what helped me deal with a memory leak; but in CER itself, I didn’t touch on this: contracts for methods suitable for CER, preparing events for calling inside CER, preparing virtual methods, using Critical Finalizers, FailFast and MemoryBarriers to ensure reliable operation of the program and prevent unmanaged resource leaks. Should I write about it or send it to MSDN?
As comrade adontz correctly noted , the problem is solved using CER.
CER is the Constrained Execution Region technology with which you can secure your code by guaranteeing that in some part of it asynchronous interruptions of the StackOverflow or ThreadAbort type are guaranteed to happen, or the jitter has “fallen off” - anything happens, and it’s not always possible to leave external resources in a consistent state.
Togo!
In other words, we can “force” the runtime to block the code section and prepare everything for its execution: check that we have enough memory on the stack and in the RAM, j-code the code, and then break it! - execute the prepared code, and then figure out if something happened.
To prevent code execution from “falling off” in the process, runtime imposes some restrictions on what _not_ can_ be done in the CER region. And, in general, just so you can’t do a lot of things:
- execute the MSIL opcode newobj;
- perform boxing (as we know, it implicitly calls newobj) (unboxing - possible);
- call virtual methods (if they were clearly not prepared - as, I will show if necessary);
- call methods through Reflection and access fields through Transparent Proxies;
- Use serialization
- use multidimensional arrays (!);
So, back to our
System.Runtime.CompilerServices.RuntimeHelpers
. His most important method for us is
PrepareConstrainedRegions
, and performs the described preparation - for example, it goes through the _ future_ call stack, executing pre-jitt and placing stop-the-world marks (now it’s clear why virtual methods and interfaces cannot be used “in the forehead” - there is dynamic dispatching and a call stack impossible until the actual execution of the code). Now the fun part. Using this method should look like this:That's right - an empty try block, and all uninterrupted operations must go in the finally block!... System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); try {} finally { // atomic-операции должны идти здесь } ...
To check - 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. Absolutely - a request for ThreadAbortException _not_ will be delayed until the end of the finally block (which does not happen due to the endless while (true) {} loop), but 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 recommends avoiding unmanaged resources. Somewhat strange advice in light of the fact that one of the goals of creating the CLR is to interact with legacy unmanaged code and resources.
So I advise you to carefully look at places of work with unmanaged 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 sorted out only what helped me deal with a memory leak; but in CER itself, I didn’t touch on this: contracts for methods suitable for CER, preparing events for calling inside CER, preparing virtual methods, using Critical Finalizers, FailFast and MemoryBarriers to ensure reliable operation of the program and prevent unmanaged resource leaks. Should I write about it or send it to MSDN?