This is the mail archive of the
guile@cygnus.com
mailing list for the Guile project.
Re: Smob documentation
- To: Jost Boekemeier <jostobfe@calvados.zrz.TU-Berlin.DE>
- Subject: Re: Smob documentation
- From: Jim Blandy <jimb@red-bean.com>
- Date: 18 Jun 1999 00:36:45 -0500
- Cc: guile@cygnus.com
- References: <199906151619.QAA23167@linux.zrz.TU-Berlin.DE>
> could someone please add a section to data-rep.texi
> that describes how and why it is neccesary to inhibit
> garbage collection while creating a smob. It took
> me one hour to find the following bug:
I don't want to recommend disabling GC, since arbitrary computation
could take place while a user is initializing an object. How about
this:
@node A Common Mistake In Allocating Smobs, Garbage Collecting Simple Smobs, Garbage Collecting Smobs, Defining New Types (Smobs)
@subsection A Common Mistake In Allocating Smobs
When constructing new objects, you must be careful that the garbage
collector can always find any new objects you allocate. For example,
suppose we wrote the @code{make_image} function this way:
@example
SCM
make_image (SCM name, SCM s_width, SCM s_height)
@{
struct image *image;
SCM image_smob;
int width, height;
SCM_ASSERT (SCM_NIMP (name) && SCM_STRINGP (name), name,
SCM_ARG1, "make-image");
SCM_ASSERT (SCM_INUMP (s_width), s_width, SCM_ARG2, "make-image");
SCM_ASSERT (SCM_INUMP (s_height), s_height, SCM_ARG3, "make-image");
width = SCM_INUM (s_width);
height = SCM_INUM (s_height);
image = (struct image *) scm_must_malloc (sizeof (struct image), "image");
image->width = width;
image->height = height;
image->pixels = scm_must_malloc (width * height, "image pixels");
/* THESE TWO LINES HAVE CHANGED: */
image->name = scm_string_copy (name);
image->update_func = scm_make_gsubr (@dots{});
SCM_NEWCELL (image_smob);
SCM_SETCDR (image_smob, image);
SCM_SETCAR (image_smob, image_tag);
return image_smob;
@}
@end example
This code is incorrect. The calls to @code{scm_string_copy} and
@code{scm_make_gsubr} allocate fresh objects. Allocating any new object
may cause the garbage collector to run. If @code{scm_make_gsubr}
invokes a collection, the garbage collector has no way to discover that
@code{image->name} points to the new string object; the @code{image}
structure is not yet part of any Scheme object, so the garbage collector
will not traverse it. Since the garbage collector cannot find any
references to the new string object, it will free it, leaving
@code{image} pointing to a dead object.
A correct implementation might say, instead:
@example
image->name = SCM_BOOL_F;
image->update_func = SCM_BOOL_F;
SCM_NEWCELL (image_smob);
SCM_SETCDR (image_smob, image);
SCM_SETCAR (image_smob, image_tag);
image->name = scm_string_copy (name);
image->update_func = scm_make_gsubr (@dots{});
return image_smob;
@end example
Now, by the time we allocate the new string and function objects,
@code{image_smob} points to @code{image}. If the garbage collector
scans the stack, it will find a reference to @code{image_smob} and
traverse @code{image}, so any objects @code{image} points to will be
preserved.