hs_garbage_collect
Alastair Reid
reid at cs.utah.edu
Wed Jun 12 09:28:01 EDT 2002
I'd like to extend the set of functions that a Haskell system provides
to C programs. As well as hs_init, hs_exit and hs_set_argv, I'd like
to have:
int hs_garbage_collect( int how_hard );
This invokes the Haskell garbage collector.
The argument and result are for use when interacting with generational
collectors.
The result is 0 if the garbage collector has done a full GC.
Increasing the size of the argument provides greater and greater
encouragement to do a full GC. Thus, this loop
int i=0; while (hs_garbage_collect(i++)) {}
is guaranteed to terminate when further GC calls will not free up any
more objects.
Subtleties:
1) What happens if the programmer writes this code instead:
int i=10; while (hs_garbage_collect(i++)) {}
^^ start deeper
It can either behave the same as if they started at 0 or
it can start by doing a hefty GC or whatever it feels like.
All that matters is that increasing the value of how_hard
makes more GC happen.
What if they write the loop backwards:
int i=10; while (hs_garbage_collect(i--)) {}
^^ start deeper ^^ decrement
They'll start with a hefty collection and then do increasingly
small collections. The subsequent (smaller) collections might not
even achieve anything but they will still return zero only if there
is no more garbage to be collected.
2) On a distributed Haskell implementation, increasing the value of
how_hard must eventually cause remote machines to do a GC too.
3) It might seem simpler to just have hs_garbage_collect do a full
collection. But this would give no efficient way to write code
that garbage collects until enough C objects have been released.
Using the above interface this would be:
int freespace;
void free(void* object, int size)
{
freespace += size;
raw_free(object);
}
void* alloc(int requirement)
{
int i=0;
while (freespace < requirement) {
if (hs_garbage_collect(i++) == 0) {
return 0; // fail
}
}
return raw_alloc(requirement);
}
4) Increasing the value of how_hard must eventually cause all
ForeignPtr finalizers for free objects to be invoked. i.e., GHC
would have to keep track of whether there are any runnable
finalizer threads and run them to completion.
Because we want to garbage collect no more than we have to, we
might want the how_hard argument to behave as follows: if there are
any pending finalizers, run one of them; otherwise do either a big
or small collection.
// Note that this version ignores the how_hard argument
// but will behave correctly anyway.
int hs_garbage_collect(int how_hard)
{
static next_depth = 0;
if (pending_finalizers > 0) { // finalize what we can
run_one_finalizer_to_completion();
return 1;
}
if (next_depth == 0) {
minor_garbage_collect();
next_depth = 1;
return 1;
} else {
major_garbage_collect();
next_depth = 0;
return (pending_finalizers > 0);
}
}
--
Alastair Reid reid at cs.utah.edu http://www.cs.utah.edu/~reid/
ps Was there any consensus about changing the hs_init API as sugegsted
last week?
More information about the FFI
mailing list