Node:Garbage Collecting Smobs, Next:A Common Mistake In Allocating Smobs, Previous:Type checking, Up:Defining New Types (Smobs)
Once a smob has been released to the tender mercies of the Scheme
system, it must be prepared to survive garbage collection. Guile calls
the mark
and free
functions of the scm_smobfuns
structure to manage this.
As described before (see Conservative GC), every object in the Scheme system has a mark bit, which the garbage collector uses to tell live objects from dead ones. When collection starts, every object's mark bit is clear. The collector traces pointers through the heap, starting from objects known to be live, and sets the mark bit on each object it encounters. When it can find no more unmarked objects, the collector walks all objects, live and dead, frees those whose mark bits are still clear, and clears the mark bit on the others.
The two main portions of the collection are called the mark phase, during which the collector marks live objects, and the sweep phase, during which the collector frees all unmarked objects.
The mark bit of a smob lives in a special memory region. When the
collector encounters a smob, it sets the smob's mark bit, and uses the
smob's type tag to find the appropriate mark
function for that
smob: the one listed in that smob's scm_smobfuns
structure. It
then calls the mark
function, passing it the smob as its only
argument.
The mark
function is responsible for marking any other Scheme
objects the smob refers to. If it does not do so, the objects' mark
bits will still be clear when the collector begins to sweep, and the
collector will free them. If this occurs, it will probably break, or at
least confuse, any code operating on the smob; the smob's SCM
values will have become dangling references.
To mark an arbitrary Scheme object, the mark
function may call
this function:
void scm_gc_mark (SCM x) | Function |
Mark the object x, and recurse on any objects x refers to. If x's mark bit is already set, return immediately. |
Thus, here is how we might write the mark
function for the image
smob type discussed above:
SCM mark_image (SCM image_smob) { /* Mark the image's name and update function. */ struct image *image = (struct image *) SCM_SMOB_DATA (image_smob); scm_gc_mark (image->name); scm_gc_mark (image->update_func); return SCM_BOOL_F; }
Note that, even though the image's update_func
could be an
arbitrarily complex structure (representing a procedure and any values
enclosed in its environment), scm_gc_mark
will recurse as
necessary to mark all its components. Because scm_gc_mark
sets
an object's mark bit before it recurses, it is not confused by
circular structures.
As an optimization, the collector will mark whatever value is returned
by the mark
function; this helps limit depth of recursion during
the mark phase. Thus, the code above could also be written as:
SCM mark_image (SCM image_smob) { /* Mark the image's name and update function. */ struct image *image = (struct image *) SCM_SMOB_DATA (image_smob); scm_gc_mark (image->name); return image->update_func; }
Finally, when the collector encounters an unmarked smob during the sweep
phase, it uses the smob's tag to find the appropriate free
function for the smob. It then calls the function, passing it the smob
as its only argument.
The free
function must release any resources used by the smob.
However, it need not free objects managed by the collector; the
collector will take care of them. The return type of the free
function should be size_t
, an unsigned integral type; the
free
function should return the number of bytes released, to help
the collector maintain statistics on the size of the heap.
Here is how we might write the free
function for the image smob
type:
size_t free_image (SCM image_smob) { struct image *image = (struct image *) SCM_SMOB_DATA (image_smob); size_t size = image->width * image->height + sizeof (*image); free (image->pixels); free (image); return size; }
During the sweep phase, the garbage collector will clear the mark bits on all live objects. The code which implements a smob need not do this itself.
There is no way for smob code to be notified when collection is complete.
It is usually a good idea to minimize the amount of processing done
during garbage collection; keep mark
and free
functions
very simple. Since collections occur at unpredictable times, it is easy
for any unusual activity to interfere with normal code.