Consider the following C++/CLI code fragment:
public ref class MyData
{
private:
UnmanagedObject* pobj;
public:
MyData()
{
pobj = ... // acquire unmanaged pointer
}
!MyData()
{
delete pobj; // finalizer
pobj = NULL;
}
void DoSomething()
{
// next line calls a native method that has no knowledge of .NET
CallToNativeMethod(data->pobj);
}
static void Test()
{
MyData^ data = gcnew MyData();
data->DoSomething()
}
}
This class represents a .NET-wrapper for some unmanaged API. In my project, I am currently dealing with a hard-to-trace bug - it seems instances of MyData
are getting garbage-collected while the method DoSomething()
is still executing. Here is what I believe happens:
- The static method
Test()
is called, which creates an instance ofMyData
- The instance method
DoSomething()
is called DoSomething()
calls a native method- While (!) the native method is being executed, the Garbage Collector collects the instance of
MyData
and calls the finalizer - The finalizer deletes
pobj
, which is used by the native method which is still executing - Well... the program crashes
My two questions:
- Is the scenario as described above actually possible? I can't quite get over the fact that an object is able to get finalized while an instance method is still executing...
- What is a good way to fix this problem (i.e. prevent the object from being collected until it is "safe" to do so)? Modifying any instance variable after the native method has executed does the trick, but it feels kind of ad-hoc.
EDIT (some additional information): the code crashes with an AccessViolationException
which bubbles up from the native method, and can be caught by try..catch
in Test()
. For debugging, I inserted a couple of logging calls, which reveal the following execution order:
- Native method is entered (but never exited)
- The finalizer is executed
- The exception is caught