How sharp is sharp?...
Ok, I just came across an article over at cross-platform.net that talks about doing precisely what I am doing with Damocles, namely, writing cross-platform native code that has a managed cross-platform binding in C#. The article mentions that my design pattern has a very subtle bug that can occur due to the fact that the C# garbage collector might kick in AFTER marshalling the args to an interop call but before the call actually finishes, causing the (admittedly highly unlikely) problem where the finalizer gets called and frees the unmanaged object too soon.
Now, this can't occur if you always dispose of your objects properly. It is slightly more likely to occur in a multithreaded scenario since the GC can run simultaneously with unmanaged code. Regardless, the solution is easy, and so I've decided to add it to the DamoclesSharp project.
It boils down to this: Instead of storing a private IntPtr, you instead store a HandleRef which contains an IntPtr. It also contains a reference to the managed object forever tying the managed and unmanaged sides together. This way, the managed object is held until the HandleRef is disposed of, thereby guaranteeing the lifetime of the original object.
Is it a bug? Is it just a theoretical possibility that would never happen in real life? It sure hasn't happened yet, but I haven't run any multithreaded tests either. Also, even though the possibility is very small, the consequences are huge. Processing memory that has been freed can result in a segfault or even cause the Blue Screen of Death if processing a buffer owned by the kernel, (say a video buffer that was wrapped).
So the code now looks like this:
So this way, we avoid getting cut with DamoclesSharp (I'm sorry, I couldn't resist the pun!)
Now, this can't occur if you always dispose of your objects properly. It is slightly more likely to occur in a multithreaded scenario since the GC can run simultaneously with unmanaged code. Regardless, the solution is easy, and so I've decided to add it to the DamoclesSharp project.
It boils down to this: Instead of storing a private IntPtr, you instead store a HandleRef which contains an IntPtr. It also contains a reference to the managed object forever tying the managed and unmanaged sides together. This way, the managed object is held until the HandleRef is disposed of, thereby guaranteeing the lifetime of the original object.
Is it a bug? Is it just a theoretical possibility that would never happen in real life? It sure hasn't happened yet, but I haven't run any multithreaded tests either. Also, even though the possibility is very small, the consequences are huge. Processing memory that has been freed can result in a segfault or even cause the Blue Screen of Death if processing a buffer owned by the kernel, (say a video buffer that was wrapped).
So the code now looks like this:
public class Image : IDisposable {
private HandleRef native=NULL;
public Image() { ... }
public Image(IntPtr NativePtr) { native=new HandleRef(this,NativePtr); ... }
~Image() {....}
#region IDisposable implementation
...
#endregion
#region Interop calls
[DllImport(Damocles.DllName)] private static extern IntPtr AllocateImage(...)
[DllImport(Damocles.DllName)] private static extern void AddImageReference(...)
[DllImport(Damocles.DllName)] private static extern void FreeImage(...)
... other Interop functions here ...
#endregion
}
So this way, we avoid getting cut with DamoclesSharp (I'm sorry, I couldn't resist the pun!)
0 Comments:
Post a Comment
<< Home