[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scheme closures: crash during garbage collection

From: gregory benison
Subject: scheme closures: crash during garbage collection
Date: Thu, 8 Jun 2006 15:28:27 -0700

guile-gnome (up to v. 2.7.98, most recent as of this writing) can
call scm_gc_unprotect_object() during a scheme garbage collector
sweep, which is a fatal error in guile-1.8.  In earlier versions
of guile, it is not a fatal error (but still, I think you're not
supposed to do it.)

Consider the following (common) scenario in a guile-gnome program:

S1 --> G1 --> G2 --> S2


G1 is a widget;
S1 is the scheme binding for widget G1;
G2 is a closure (callback) connected to one of the signals of G1;
S2 is the scheme procedure implementing closure G2.

Clearly, object S2 should clearly be protected from scheme's garbage
collector as long as it is in use as a callback.  Ideally, during the
mark phase of garbage collection, the marking would propagate from S1
through to S2, but that is difficult because G1 and G2 know nothing of
scheme or its garbage collector. The solution adopted by guile-gnome
is to add a persistent mark to S2 by calling scm_gc_protect_object()
on it when G2 is created, and to call scm_gc_unprotect_object() on S2
when G2 is finalized.

Now consider the following sequence of events:

1) S1 is garbage collected;
2) that was the last reference to G1, so it is finalized;
3) that was the last reference to G2, so it is finalized;
4) During its finalization, (in gclosure.c) G2 calls
   scm_gc_unprotect_object() on S2...

but this is an error and causes program termination with guile-1.8 (and I think
undefined behavior with earlier guile versions.)

This behavior is difficult to observe in practice with signals connected to
gtk widgets (with their floating references) because, in practice, gtk widgets
don't ever seem to get garbage-collected by scheme; e.g. in the following code,
the button object and its attached closure don't ever get garbage-collected,
even though scheme has no reference to them:

(let ((button (make <gtk-button>)))
   (connect button 'clicked (lambda args #f)))

The fatal error is much more easily observed with gobjects that don't use floating
references, for example if one creates a non-widget gobject and then connects
scheme callbacks to its signals (which is how I first observed the error).  The
behavior can also be seen using the native 'gobject' type, as in the following
innocuous-looking code:

;; this code is fatal in guile-1.8
(define obj (make <gobject>))
(connect obj 'notify (lambda args #f))
(set! obj #f)
(gc) ;; keep calling gc until the error occurs

Here is a stack trace:

#0  0x0047a0b6 in abort () from /lib/tls/
#1  0x00b21987 in scm_gc_unprotect_object (obj=0xb69bf8f8) at gc.c:786
#2  0x00be0737 in free_closure (data="" closure=0xa0a7b60) at gclosure.c:106
#3  0x00f4081c in IA__g_closure_invalidate (closure=0xa0a7b60) at gclosure.c:123
#4  0x00f40949 in IA__g_closure_unref (closure=0xa0a7b60) at gclosure.c:341
#5  0x00f517df in IA__g_signal_handlers_destroy (instance=0xa1bc308) at gsignal.c:628
#6  0x00f427ad in g_object_real_dispose (object=0xa1bc308) at gobject.c:523
#7  0x00f429ee in IA__g_object_unref (_object=0xa1bc308) at gobject.c:558
#8  0x00be503e in scm_gtype_instance_free (smob=0xb7d56920) at gtype.c:468
#9  0x00b23ec5 in scm_i_sweep_card (p=0xb7d56920, free_list=0xbff81668, seg=0xa0b6080) at gc-card.c:234
#10 0x00b22d1d in scm_i_sweep_some_cards (seg=0xa0b6080) at gc-segment.c:164
#11 0x00b22dfd in scm_i_sweep_segment (seg=0xa0b6080) at gc-segment.c:206
#12 0x00b22e71 in scm_i_sweep_all_segments (reason=0xb7fe25 "GC") at gc-segment.c:223
#13 0x00b22168 in scm_i_gc (what=0xb7fe2e "call") at gc.c:574
#14 0x00b223c3 in scm_gc () at gc.c:454

I propose the following alternative strategy, which does not cause the
fatal error:

- When creating a gclosure from a scheme procedure, instead of calling
  scm_gc_protect_object() on the scheme procedure, add the scheme
  procedure to a 'master list' of all scheme procedures being used
  in this way.
- Ensure that all such procedures on the master list are protected
  from scheme's garbage collector.  This is easily accomplished by
  making sure that scheme has a reference to the master list itself
  and that the master list, when it is marked, also marks all of its
- When a gclosure is finalized, instead of calling
  scm_gc_unprotect_object() on the scheme function, add the function
  to a "defunct list" of scheme procedures that no longer need
- At some convenient time, say from a glib idle function, remove all
  items on the "defunct list" from the "master list"; next time scheme's
  garbage collector runs, it can free the scheme functions that are
  no longer being used as callbacks.

I have a working implementation of this strategy which I will make available
(following discussion).

Gregory Benison
Oregon State University

reply via email to

[Prev in Thread] Current Thread [Next in Thread]