Next: , Previous: Garbage Collecting Simple Smobs, Up: Defining New Types (Smobs)


4.4.6 Remembering During Operations

It's important that a smob is visible to the garbage collector whenever its contents are being accessed. Otherwise it could be freed while code is still using it.

For example, consider a procedure to convert image data to a list of pixel values.

     SCM
     image_to_list (SCM image_smob)
     {
       struct image *image;
       SCM lst;
       int i;
     
       scm_assert_smob_type (image_tag, image_smob);
     
       image = (struct image *) SCM_SMOB_DATA (image_smob);
       lst = SCM_EOL;
       for (i = image->width * image->height - 1; i >= 0; i--)
         lst = scm_cons (scm_from_char (image->pixels[i]), lst);
     
       scm_remember_upto_here_1 (image_smob);
       return lst;
     }

In the loop, only the image pointer is used and the C compiler has no reason to keep the image_smob value anywhere. If scm_cons results in a garbage collection, image_smob might not be on the stack or anywhere else and could be freed, leaving the loop accessing freed data. The use of scm_remember_upto_here_1 prevents this, by creating a reference to image_smob after all data accesses.

There's no need to do the same for lst, since that's the return value and the compiler will certainly keep it in a register or somewhere throughout the routine.

The clear_image example previously shown (see Type checking) also used scm_remember_upto_here_1 for this reason.

It's only in quite rare circumstances that a missing scm_remember_upto_here_1 will bite, but when it happens the consequences are serious. Fortunately the rule is simple: whenever calling a Guile library function or doing something that might, ensure that the SCM of a smob is referenced past all accesses to its insides. Do this by adding an scm_remember_upto_here_1 if there are no other references.

In a multi-threaded program, the rule is the same. As far as a given thread is concerned, a garbage collection still only occurs within a Guile library function, not at an arbitrary time. (Guile waits for all threads to reach one of its library functions, and holds them there while the collector runs.)