Node:A Common Mistake In Allocating Smobs, Next:, Previous:Garbage Collecting Smobs, Up:Defining New Types (Smobs)



18.3.5 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 make_image function this way:

SCM
make_image (SCM name, SCM s_width, SCM s_height)
{
  struct image *image;
  SCM image_smob;
  int width, height;

  SCM_ASSERT (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_c_define_gsubr (...);

  SCM_NEWCELL (image_smob);
  SCM_SET_CELL_WORD_1 (image_smob, image);
  SCM_SET_CELL_TYPE (image_smob, image_tag);

  return image_smob;
}

This code is incorrect. The calls to scm_string_copy and scm_c_define_gsubr allocate fresh objects. Allocating any new object may cause the garbage collector to run. If scm_c_define_gsubr invokes a collection, the garbage collector has no way to discover that image->name points to the new string object; the 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 image pointing to a dead object.

A correct implementation might say, instead:

  image->name = SCM_BOOL_F;
  image->update_func = SCM_BOOL_F;

  SCM_NEWCELL (image_smob);
  SCM_SET_CELL_WORD_1 (image_smob, image);
  SCM_SET_CELL_TYPE (image_smob, image_tag);

  image->name = scm_string_copy (name);
  image->update_func = scm_c_define_gsubr (...);

  return image_smob;

Now, by the time we allocate the new string and function objects, image_smob points to image. If the garbage collector scans the stack, it will find a reference to image_smob and traverse image, so any objects image points to will be preserved.