Reproducing the Finalization Timing Problem

At the end of the day, the debug-related problem just described is neither critical nor difficult to solve. The finalization timing problem, however, is a more serious one. To demonstrate this problem in a reproducible way, assume the wrapper class shown here:

// ManagedWrapper2.cpp

// build with "CL /LD /clr ManagedWrapper2.cpp" #include "XYZ.h"

#pragma comment(lib, "XYZLib.lib") #include <windows.h>

public ref class XYZConnection {

HXYZ hxyz;

public: XYZConnection()

double GetData() {

return ::XYZGetData(this->hxyz); // XYZGetData needs 1 second to execute

~XYZConnection() {

!XYZConnection() {

System::Console::WriteLine("In finalizer now!");

::XYZDisconnect(hxyz);

A client application that causes the finalization timing problem is shown here. This program creates a thread that sleeps for 1/2 second and causes a garbage collection after that. While the thread is sleeping, an instance of the XYZConnection wrapper is created and GetData is called.

// ManagedClient2.cpp

// compile with "CL /clr ManagedClient2.cpp"

#using "ManagedWrapper2.dll"

using namespace System;

using namespace System::Threading;

void ThreadMain() {

// pretend some work here Thread::Sleep(500);

// assume the next operation causes a garbage collection by accident GC::Collect();

// to demonstrate the timing problem, start another thread that

// causes GC after half a second

Thread t(gcnew ThreadStart(&ThreadMain));

XYZConnectionA cn = gcnew XYZConnection();

// call cn->GetData() before the second is over // (remember that XYZGetData runs ~ 1 second) double data = cn->GetData();

System::Console::WriteLine("returned data: {0}", data);

// ensure that the thread has finished before you dispose it t.Join();

Notice that in this application, a programmer does not dispose the XYZConnection object. This means that the finalizer is responsible for cleaning up the native resource. The problem with this application is that the finalizer is called too early. The output of the program is shown here:

processing XYZConnect processing XYZGetData

...pretending some work... In finalizer now! processing XYZDisconnect finished processing XYZGetData returned data: 42

As this output shows, the finalizer calls the native cleanup function XYZDisconnect while the native worker function XYZGetData is using the handle. In this scenario, the finalizer is called too early.

This timing problem occurs because of the optimization that the JIT compiler does for root references on the stack. In main, the GetData method of the wrapper class is called:

To call this function, the cn variable is passed as the this tracking handle argument of the function call. After the argument is passed, the cn variable is no longer used. Therefore, cn is no longer a root reference. Now, the only root reference to the XYZConnection object is the this parameter of the GetData function:

double GetData() {

return ::XYZGetData(this->hxyz);

In GetData, this last root reference is used to retrieve the native handle. After that, it is no longer used. Therefore, the this parameter is no longer a root reference when XYZGetData is called. When a garbage collection occurs while XYZGetData executes, the object will be finalized too early. The sample program enforces this problem scenario by causing a garbage collection from the second thread before XYZGetData returns. To achieve this, XYZGetData sleeps 1 second before it returns, whereas the second thread waits only 1/2 second before it calls GC::Collect.

Was this article helpful?

0 0

Post a comment