www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - reference to delegates and garbage collection

reply "AnFive" <example example.com> writes:
Hello everybody. I am new to D, and I am trying to familiarize
with all the new (to me) features.
I have a question about a strange behavior I cannot understand.
(In case it matters, I am using gdc 2.065 for Windows, but the
same happens with dmd).

Example code :

class A {	
	void delegate() del;
		
	~this() { writeln("A Destructor called"); }	
}

class B {	
	void fun() {}	
}

A makeA() {	
	auto a = new A();
	a.del = &(new B().fun);
	return a;
}

void main() {
		
	auto a = makeA();
          /* Magic line goes here, see below */
	a = null;
	
	foreach(i; 0..10) {
		writeln("Collecting");
		GC.collect();		
	}
	
	writeln("End");	
}

If I run this code, the instance of A is of course collected at
the first call to GC.collect().
But if I call the delegate (i.e. a.del() ) in the "magic line",
the instance is not destroyed until the end of the main. If I
"wrap" the call to a.del() in another function or method, the
instance is collected.

Can somebody explain why this happens? Is it just a strangeness
of the GC? Does calling the delegate keep a reference to a in the
current scope? Am I going crazy? I spent about half an hour
looking for a non-existent memory leak in my program because of
this behavior, so I'd like to have an explanation.

Thanks in advance!
Jul 14 2014
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
I'm just guessing, but it looks to me that the delegate's pointer 
might be on the stack there and isn't overwritten when the one 
function returns, so the gc still thinks there might be an active 
pointer to it.
Jul 14 2014
prev sibling parent "Kagamin" <spam here.lot> writes:
Since you access a field through `a` instance, this is usually 
done by loading the instance address into some register and 
reading from a location at a certain offset from that register

mov esi, [ebp-4] # the instance address
mov eax, [esi+8] # first field
...
mov [ebp-4], 0 # clear stack variable
...
call collect
...
ret # from main


collect:
...
push esi
...
pop esi
ret


Note the instance address is preserved in a register even if it's 
not needed. And this happen only if you read a field.
Jul 15 2014