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