2

We're dealing with the GC being too quick in a .Net program. Because we use a class with native resources and we do not call GC.KeepAlive(), the GC collects the object before the Native access ends. As a result the program crashes. We have exactly the problem as described here:

Does the .NET garbage collector perform predictive analysis of code?

Like so:

{  var img = new ImageWithNativePtr();
   IntPtr p = img.GetData();
   // DANGER!
   ProcessData(p);
}

The point is: The JIT generates information that shows the GC that img is not used at the point when GetData() runs. If a GC-Thread comes at the right time, it collects img and the program crashes. One can solve this by appending GC.KeepAlive(img); Unfortunately there is already too much code written (at too many places) to rectify the issue easily.

Therefore: Is there for example an Attribute (i.e. for ImageWithNativePtr) to make the JIT behave like in a Debug build? In a Debug build, the variable img will remain valid until the end of the scope ( } ), while in Release it looses validity at the comment DANGER.

Community
  • 1
  • 1
Hui
  • 41
  • 2
  • I believe `GC.KeepAlive` is your only option here. – Cory Nelson Aug 01 '11 at 12:32
  • Doesn't your unmanaged resource need some kind of disposing? – Anton Tykhyy Aug 01 '11 at 12:34
  • 1
    Code that depends on a stupid implementation is broken. –  Aug 01 '11 at 12:46
  • This is why the SafeHandle class exists. The band-aid you are looking for has to be ripped off quickly. Solve this the right way. – Hans Passant Aug 01 '11 at 12:52
  • The Dispose / Finalizer of the Image object releases the memroy - therefore the app crashes. It wasn't my design - I just have to fix it. Unfortunately there are pitfalls like shared objects etc. – Hui Aug 02 '11 at 10:58

3 Answers3

2

To the best of my knowledge there is no way to control jitter's behavior based on what types a method references. You might be able to attribute the method itself, but that won't fill your order. This being so, you should bite the bullet and rewrite the code. GC.KeepAlive is one option. Another is to make GetData return a safe handle which will contain a reference to the object, and have ProcessData accept the handle instead of IntPtr — that's good practice anyway. GC will then keep the safe handle around until the method returns. If most of your code has var instead of IntPtr as in your code fragment, you might even get away without modifying each method.

Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • The most expressive syntax (instead of KeepAlive) if a safe handle / dedicated object is not possible would be to make `ImageWithNativePtr` implement `IDisposable` and wrap the code in `using (var img = new ImageWithNativePtr()) { ... }` – Julien Roncaglia Aug 01 '11 at 12:45
  • Indeed, but that would require more code changes. BTW the whole `ImageWithNativePtr` could derive from `SafeHandle`. – Anton Tykhyy Aug 01 '11 at 14:43
  • The IDisposeable strategy was of course thought thru - however that requires heaps of code changes. The only thing to hold on yet is still the scoping of the instructions. But as we know the GC does a quicker job. – Hui Aug 02 '11 at 11:01
1

You have a few options.

  1. (Requires work, more correct) - Implement IDisposable on your ImageWithNativePtr class as it compiles down to try { ... } finally { object.Dispose() }, which will keep the object alive provided you update your code with usings. You can ease the pain of doing this by installing something like CodeRush (even the free Xpress supports this) - which supports creating using blocks.
  2. (Easier, not correct, more complicated build) - Use Post Sharp or Mono.Cecil and create your own attribute. Typically this attribute would cause GC.KeepAlive() to be inserted into these methods.

The CLR has nothing built in for this functionality.

Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
0

I believe you can emulate what you want with a container that implements IDispose, and a using statement. The using statement allows for defining the scope and you can place anything you want in it that needs to be alive over that scope. This can be a helpful mechanism if you have no control over the implementation of ImageWithNativePtr.

The container of things to dispose is a useful idiom. Particularly when you really should be disposing of something ... which is probably the case with an image.

using(var keepAliveContainer = new KeepAliveContainer())
{
  var img = new ImageWithNativePtr();
  keepAliveContainer.Add(img);
  IntPtr p = img.GetData();
  ProcessData(p);
  // anything added to the container is still referenced here.
}
Steve Steiner
  • 5,299
  • 4
  • 32
  • 43