Problem with Garbage Collection

S

Stephen Engle

I have an app that I am working on that is dependent on an unmanaged code
library. There is a middle library of managed C++ that encapsulates the
unmanaged libary functions calls into a class.

What is happening is that I create an instance of this class in a separate
thread and use it to call certain functions. But when I call GC.Collect
(which is necessary for some timely cleanup) from the main thread, the
garbage collector deletes the class from my first worker thread before the
functions that the thread is calling have finished.

I'm hoping to find a way to prevent this.

My code resembles the following though I have a couple different worker
threads and the timer function calls each of them according to different
timing settings. Specifically, it's the DLMngr object in the worker thread
function GetDLAList that gets collected before the GetObjectList function
from the underlying library finishes.

I cannot understand how that can happen since the DLMngr object is calling
the function GetObjectList. This does happen to more than one of my worker
threads but since most of them only call one function in the underlying
library, they do not have adverse results. But one calls several functions
and each of them checks for data initialized when the DLMngr object is
created. If that data is not there the function fails.

Can anyone tell me why these objects are being collected before the
functions are finished executing?

public unsafe class WorkerThreadA
{
public static int * Result;
public static System.IntPtr ResultPtr;

private static void GetDLAList()
{
int result=-1;
DLMngr dlm=new DLMngr();

if(Result!=null)
{
result=dlm.GetObjectList();
*Result=result;
}
dlm=null;
}

public static void ThreadGetDLAList(System.IntPtr result, ref Thread
threada)
{
ThreadStart thread;

Result=(int *)result.ToPointer();
ResultPtr=result;
thread=new ThreadStart(GetDLAList);
threada=new Thread(thread);
threada.Start();
}
}



public class Form1 : System.Windows.Forms.Form
{

...


private Thread ThreadObject=null;
private ThreadStart TSObject=null;
private GCHandle GCHListObjectNew=new GCHandle();
private ArrayList ArrayObjectCurrent=new ArrayList();
private ArrayList ArrayObjectNew=new ArrayList();
private int ListObjectCurrent=-1;
private int ListObjectNew=-1;


...


public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
GCHListObjectNew=GCHandle.Alloc(ListObjectNew, GCHandleType.Pinned);
}


...


private unsafe void TimerUpdate_Tick(object sender, System.EventArgs e)
{
String objectid=new String('c', 10);
int errorval;
FileTime ft=new FileTime();
System.IntPtr sipr;
SystemTime st=new SystemTime();
TimeSpan tsobject=new TimeSpan(0, 0, 6, 0, 0);
TimeSpan tsdisp;
System.Int32 checkr;

// check for new object list
//
checkr=(System.Int32)GCHListObjectNew.Target;
if(ListObjectNew!=checkr) ListObjectNew=checkr;
if(ListObjectCurrent!=ListObjectNew)
{
// remove thread data since thread is finished
//
if(ThreadObject!=null) ThreadObject=null;
if(TSObject!=null) TSObject=null;
GC.Collect();

// update data returned by thread
//
ListObjectCurrent=ListObjectNew;
ArrayObjectNew.Clear();
gblitem=DLM.UserFirstItem(ListObjectCurrent, ref objectid, &errorval);
while(gblitem==true && errorval==0)
{
ArrayRecipientNew.Add(new DataObject(objectid));
gblitem=DLM.UserNextItem(ListObjectCurrent ref objectid, &errorval);
}
UpdatingObjects=false;
ArrayObjectNew.Sort();
}

LibWrap.GetSystemTime(st);
LibWrap.SystemTimeToFileTime(st, ft);
lt=ft.highdatetime;
lt=lt<<32;
lt=lt+ft.lowdatetime;
DateTime dt=new DateTime(lt);

// check for time to update object list
//
tsdisp=dt-LastUpdateObject;
if(dt-LastUpdateObject>tsobject && UpdatingObjectss==false)
{
UpdatingObjectss=true;
sipr=GCHListObjectNew.AddrOfPinnedObject();
WorkerThreadA.ThreadGetObjectList(sipr, ref ThreadObject, ref TSObject);
LastUpdateObject=dt;
}
}


}
 
G

Guest

Take a look at the fixed statement's documentation: "The fixed statement sets
a pointer to a managed variable and "pins" that variable during the execution
of statement. Without fixed, pointers to managed variables would be of little
use since garbage collection could relocate the variables unpredictably."
 
?

=?ISO-8859-1?Q?Lasse_V=E5gs=E6ther_Karlsen?=

Stephen said:
I have an app that I am working on that is dependent on an unmanaged code
library. There is a middle library of managed C++ that encapsulates the
unmanaged libary functions calls into a class.

What is happening is that I create an instance of this class in a separate
thread and use it to call certain functions. But when I call GC.Collect
(which is necessary for some timely cleanup) from the main thread, the
garbage collector deletes the class from my first worker thread before the
functions that the thread is calling have finished.
private static void GetDLAList()
{
int result=-1;
DLMngr dlm=new DLMngr();

if(Result!=null)
{
result=dlm.GetObjectList();
*Result=result;
}
dlm=null;
}
<snip>

Try adding a call to GC.KeepAlive(dlm) at the end of that method, before
"dlm=null;" which is unnecessary btw since the reference to the object
will disappear when GetDLAlist returns.

The specific purpose of GC.KeepAlive is to add a line of code that won't
be optimized away and thus keeping a point in your code where you're
guaranteed to have a reference to the object in that variable.

The GC is very aggressive, and couple this with the optimizer which
might notice that there's really no point in setting dlm to null, and
you have the problem that the object in dlm is collected before
GetObjectList returns, simply because that's the last point in your code
that dlm is actually needed (as far as GC/optimizer can figure out).

GC.KeepAlive counters that problem.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top