Variable scope not what is expected?

G

Guest

Hi all,

I have some code similar to the following:

C#
MyClass mVer = ne w MyClass();

…in a member function
mVar.SendEvent( new Event() );

Managed C++

Ref class MyClass
{
Void SendEvent( Event^ event )
{
// event is collected by the GC during this call... why?
mUnmanagedPtr->SendUnmanagedEvent( event->GetUnmanagedEvent() );
}
}

For some reason the GC thinks its ok to collect the managed event object
before SendUnmanagedEvent returns. Standard scope rules dictate that event is
referenced until the C# mVar.SendEvent function returns. At least that’s my
understanding. Because of this strange behavior I get random crashes when the
GC occasionally collects my events during the call.

Is this a bug? Is my expectation wrong? Am I doing something wrong?
 
P

Peter Duniho

Scythen said:
[...]
Ref class MyClass
{
Void SendEvent( Event^ event )
{
// event is collected by the GC during this call... why?
mUnmanagedPtr->SendUnmanagedEvent( event->GetUnmanagedEvent() );
}
}

For some reason the GC thinks its ok to collect the managed event object
before SendUnmanagedEvent returns. Standard scope rules dictate that event is
referenced until the C# mVar.SendEvent function returns. At least that’s my
understanding. Because of this strange behavior I get random crashes when the
GC occasionally collects my events during the call.

Is this a bug? Is my expectation wrong? Am I doing something wrong?

It's not a bug. Your expectation is wrong. But, your misunderstanding
is not all that unusual or even unsurprising.

The GC will collect any reference that is no longer in use. The problem
comes in determining "use". As you've found, "use" is not defined
according to scope. It is literally defined according to "use".

So, in the code you posted, the object instance is not referred to by
any code after it's used to obtain the unmanaged event data (via
GetUnmanagedEvent()). The GC knows this and believes that since you are
not using that object any more, there is no need to keep it around.

To fix the issue, you need to explicitly tell the GC that the lifetime
of the object needs to be extended. You can do this by adding a
statement reading "GC.KeepAlive(event);" after your call to
SendUnmanagedEvent(). This ensures that the reference is not considered
unused until after that statement, and so won't be collected during the
call (or any other statements prior to that statement).

Pete
 
J

Jon Skeet [C# MVP]

Scythen said:
I have some code similar to the following:

C#
MyClass mVer = ne w MyClass();

?in a member function
mVar.SendEvent( new Event() );

Managed C++

Ref class MyClass
{
Void SendEvent( Event^ event )
{
// event is collected by the GC during this call... why?
mUnmanagedPtr->SendUnmanagedEvent( event->GetUnmanagedEvent() );
}
}

For some reason the GC thinks its ok to collect the managed event object
before SendUnmanagedEvent returns. Standard scope rules dictate that event is
referenced until the C# mVar.SendEvent function returns. At least that?s my
understanding. Because of this strange behavior I get random crashes when the
GC occasionally collects my events during the call.

Is this a bug? Is my expectation wrong? Am I doing something wrong?

Your expectation is incorrect.

The garbage collector is free to collect the object as soon as the JIT
can tell that the reference itself is no longer used.

Now, in your code that means that unless event->GetUnmanagedEvent()
returns something which in turn references event itself, there's
nothing referencing the Event object any more and it can be garbage
collected.

From the Unified C# 3.0 spec:

<quote>
2. If the object, or any part of it, cannot be accessed by any
possible continuation of execution, other than the running of
destructors, the object is considered no longer in use, and it becomes
eligible for destruction. The C# compiler and the garbage collector may
choose to analyze code to determine which references to an object may
be used in the future. For instance, if a local variable that is in
scope is the only existing reference to an object, but that local
variable is never referred to in any possible continuation of execution
from the current execution point in the procedure, the garbage
collector may (but is not required to) treat the object as no longer in
use.
</quote>
 
L

Laura T.

Scythen said:
Hi all,

I have some code similar to the following:

C#
MyClass mVer = ne w MyClass();

…in a member function
mVar.SendEvent( new Event() );

Managed C++

Ref class MyClass
{
Void SendEvent( Event^ event )
{
// event is collected by the GC during this call... why?
mUnmanagedPtr->SendUnmanagedEvent(
event->GetUnmanagedEvent() );
}
}

For some reason the GC thinks its ok to collect the managed event object
before SendUnmanagedEvent returns. Standard scope rules dictate that event
is
referenced until the C# mVar.SendEvent function returns. At least that’s
my
understanding. Because of this strange behavior I get random crashes when
the
GC occasionally collects my events during the call.

Is this a bug? Is my expectation wrong? Am I doing something wrong?

GC is thinking in this way:

SendEvent
0. ref event
1. new tempvar=ref event->GetUnmanagedEvent()
2. ref event=null (we have now the tempvar so event is no longer needed,
"used")
3. ref function=SendUnmanagedEvent -> Call (tempvar is rooted to
SendEvent)
5. tempres=null
6. function=null
 

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