Understanding HXCPP GC finalizers / object lifetimes

390 views
Skip to first unread message

Jonathan Hardie

unread,
Nov 3, 2014, 8:10:53 PM11/3/14
to haxe...@googlegroups.com
Hello, I am trying to create a CFFI extern lib with HXCPP. (Latest git versions of hxcpp and haxe)

I have a need to return a float* created on the C side with malloc to the Haxe side, where I want to store a reference to it as a member variable of the class that uses it.  This float pointer is required by a third-party C lib which I'm interfacing with (NanoVG, for manipulating transformation matrixes)

I can't work out how to link the lifetime of the Haxe class instance with the lifetime of this piece of externally malloc'd memory.
I can return the 'value' type to Haxe code by wrapping my float* 

float* myMem = (float*)malloc(sizeof(float) * 6);
with 
value wrappedMem = alloc_abstract(k_type, myMem);
- And this works great on the Haxe side, until the time comes when I want to kill the object holding onto this wrappedMem, and free this memory.

I try adding:

val_gc(wrappedMem, myFinalizerFn);

But myFinalizerFn runs the first time the GC runs, and the value is collected – even though I've passed a reference back into Haxe code, where the instance holding it is still alive.

I'm guessing the GC only keeps track of lifetimes of local variables there, so after the Haxe function where I assign the value to a member var has exited, it thinks it's okay to collect it.

Then I tried within Haxe code:

Gc.setFinalizer(parentObj, Function.getProcAddress("", "deallocateMem__1"));

Where parentObj is the object holding onto the value – the object whose lifetime I want to link this memory to.
It seems like the finalizer function should get called when parentObj get's GC'd - but again, it runs with the first GC collection, even though parentObj is very much alive.

(dellocateMem__1 is a CFFI function loaded with cpp.Lib.load(), as that was the only kind of reference that Gc.setFinalizer seemed to like)

Do I have some fundamental misunderstanding of how this is supposed to work?

Any help on this will be greatly appreciated!

Cheers,
Jonathan



Jonathan Hardie – Web Developer

Hugh

unread,
Nov 4, 2014, 12:03:07 AM11/4/14
to haxe...@googlegroups.com
Hi,
Your val_gc technique should work - I use this all the time.  So if there is a bug, it is to do with where your wrappedMem Dynamic is stored in the haxe code.  If this is stored as a member, then is the containing object (parentObj) referenced from elsewhere?  You say it is alive, but hxcpp does not think so, so it is a matter of working out why.  If you make a static variable refer to the parent object, then you can be pretty sure the gc will keep it alive.
The finalizer call can take static haxe functions, but it is a bit fiddly, and you can't do any memory allocations inside the finalizer:


       class Test {
               
var handle:String;
               
public function new() {
               handle
= "free me";
               cpp
.vm.Gc.setFinalizer(this,cpp.Function.fromStaticFunction(destroy));
               
}
             
               
@:void public static function destroy(inTest:Test): Void {
              untyped __cpp__
('printf(\"Free %s\\\\n\", inTest->handle.__s)');
               
}
          
}

Hugh

Jonathan Hardie

unread,
Nov 5, 2014, 10:58:12 AM11/5/14
to haxe...@googlegroups.com
Hi Hugh, thanks for the quick reply.  I’m still scratching my head over this though, it doesn’t seem to be working the way you describe for me.

I’ve made a reduced test case you can look at here: https://github.com/jonathanhardie/HxCPP-GC-Test

In it, I allocate two external abstracts with alloc_abstract.  One of them is assigned to a member var of the main class, another is assigned to a member var of a child class, which is in turn referenced by a member var of the main class.
I collect some garbage using Gc.run().  The handle held by the child is collected (even though it is still held), while the one held by the main class is not.  Even if I explicitly set handle = null on the main class, and run another collection, that handle is still not collected.

This is on OSX 10.9, running latest hxcpp git and latest haxe git, compiling with GCC 4.9

Any light you can shed on what I might be doing wrong would be awesome!

Thanks,
Jonathan

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Hugh

unread,
Nov 5, 2014, 11:41:17 PM11/5/14
to haxe...@googlegroups.com
Hi,

Your  getExternalHandle:Void->Pointer<Float32> should in fact be
 getExternalHandle:Void->Dynamic
Basically, "value" in ndll code maps to "Dynamic".

The returned value from this function (ie the "abstract" from alloc_abstract)  "owns" the matrix.  So the value is returned, you get the memory pointer out, and then the owner is no longer stored anywhere so it releases your memory.
So probably best to store the returned handle as well to make sure it stays alive.  Alternatively, you could remove the val_gc call and explicitly free the memory via some haxe code, but this would be error prone.

In the first case, where you go not get a GC when you expect, it is probably because hxcpp uses "conservative" gc, and there could be a stray reference to the value somewhere in the machine internals.  When experimenting with forced GC, it is usually best to call GC.run from a "parent function" of the function that did the actual allocation - ie, after the function has "returned". The GC only guarantees that no live object will be lost, not that all dead objects will be collected.

Hugh

Jonathan Hardie

unread,
Nov 6, 2014, 5:57:21 AM11/6/14
to haxe...@googlegroups.com
Perfect, that’s exactly what I was missing, thank you!

Daniel Glazman

unread,
Nov 18, 2014, 5:43:35 AM11/18/14
to haxe...@googlegroups.com
On 04/11/2014 06:03, Hugh wrote:

> The finalizer call can take static haxe functions, but it is a bit
> fiddly, and you can't do any memory allocations inside the finalizer:
>
> class Test{
> var handle:String;
> public function new(){
> handle ="free me";
> cpp.vm.Gc.setFinalizer(this,cpp.Function.fromStaticFunction(destroy));
> }
>
> @:void public static function destroy(inTest:Test):Void{
> untyped __cpp__('printf(\"Free %s\\\\n\", inTest->handle.__s)');
> }
> }

This does not work with 3.1.3 and when I try to use it with the
trunk, I get a "fromStaticFunction must take a static function" build
error.

</Daniel>

wighawag

unread,
Nov 18, 2014, 6:32:04 AM11/18/14
to haxe...@googlegroups.com
Hi,
try to get the latest version, I have verified that it was fixed few days ago : see https://github.com/HaxeFoundation/haxe/issues/3538

--- You received this message because you are subscribed to the Google Groups "Haxe" group.

Daniel Glazman

unread,
Nov 18, 2014, 7:10:56 AM11/18/14
to haxe...@googlegroups.com
On 18/11/2014 12:31, wighawag wrote:
> Hi,
> try to get the latest version, I have verified that it was fixed few
> days ago : see https://github.com/HaxeFoundation/haxe/issues/3538

Unfortunately, the latest nightly builds on Mac are more than a month
old and I hit the following fatal error when I try to build from the
trunk:

sh: camlp4o: command not found
File "parser.ml", line 1:
Error: Error while running external preprocessor

</Daniel>

Jonathan Hardie

unread,
Nov 18, 2014, 8:20:36 AM11/18/14
to haxe...@googlegroups.com
Hi Daniel, here's a fresh mac haxe build I just made: https://www.dropbox.com/s/wgwwn4dvu8hbxnb/haxe.zip?dl=0 (Just the executable)
Give it a shot.  FWIW, I have Ocaml installed via homebrew, seems to work pretty well for me.

Cheers,
Jonathan

Daniel Glazman

unread,
Nov 18, 2014, 9:03:36 AM11/18/14
to haxe...@googlegroups.com
On 18/11/2014 14:20, Jonathan Hardie wrote:

> Hi Daniel, here's a fresh mac haxe build I just
> made: https://www.dropbox.com/s/wgwwn4dvu8hbxnb/haxe.zip?dl=0 (Just the
> executable)
> Give it a shot. FWIW, I have Ocaml installed via homebrew, seems to
> work pretty well for me.

Ah, cool, thanks a lot Jonathan. After a fresh build of hxcpp's trunk,
it works fine now. Thanks again!

</Daniel>

Daniel Glazman

unread,
Nov 18, 2014, 9:07:22 AM11/18/14
to haxe...@googlegroups.com
On 18/11/2014 14:20, Jonathan Hardie wrote:
> Hi Daniel, here's a fresh mac haxe build I just
> made: https://www.dropbox.com/s/wgwwn4dvu8hbxnb/haxe.zip?dl=0 (Just the
> executable)
> Give it a shot. FWIW, I have Ocaml installed via homebrew, seems to
> work pretty well for me.


Ok, your built worked fine because you already had ocaml installed.
I had not. I suggest someone adds a reference to

https://github.com/ocaml/camlp4

in the Build Haxe pages...

</Daniel>
Reply all
Reply to author
Forward
0 new messages