In the following freehand code I'm concerned that Idris can garbage collect the Foo pointer while it's still in use. I have had mixed experience with using this pattern. Sometimes I get memory errors, sometimes not. But when I did debug it it seemed to fall over after the first time the Idris GC ran.
struct Foo { int n; };
Foo* mkfoo () {
return new Foo{0};
}
int slow (Foo* foo) {
usleep(9999999999); // Idris runs GC here
return foo->n; // foo no longer allocated
}
data Foo = MkFoo GCAnyPtr
%foreign "mkfoo"
mkfoo : PrimIO AnyPtr
foo : IO Foo
foo = do
foo <- primIO mkfoo
foo <- onCollectAny foo free
pure (MkFoo foo)
%foreign "slow"
prim__slow : GCAnyPtr -> Int
slow : Foo -> Int
slow (MkFoo foo) = prim__slow foo
main : IO ()
main = do
foo <- mkfoo
printLn (slow foo)
If this is a bug, perhaps it's OK if it remains as a limitation of finalisers onCollect[Any], if it's documented VERY LOUDLY cos it defeats the whole purpose of the function when used as above, i.e. safe automatic memory management. That said, I can't see when you'd use it in any other way. Would you ever have a pointer that you don't pass back to C? In what scenario are finalisers safe?
In the following freehand code I'm concerned that Idris can garbage collect the
Foo
pointer while it's still in use. I have had mixed experience with using this pattern. Sometimes I get memory errors, sometimes not. But when I did debug it it seemed to fall over after the first time the Idris GC ran.If this is a bug, perhaps it's OK if it remains as a limitation of finalisers
onCollect[Any]
, if it's documented VERY LOUDLY cos it defeats the whole purpose of the function when used as above, i.e. safe automatic memory management. That said, I can't see when you'd use it in any other way. Would you ever have a pointer that you don't pass back to C? In what scenario are finalisers safe?