Re: [Gc] Two-phase finalization?

David Kastrup <dak-mXXj517/[email protected]> Sun, 08 Nov 2015 22:42:03 +0100
Newsgroups gmane.comp.programming.garbage-collection.boehmgc
Message-ID <[email protected]>
Florian Weimer <[email protected]> writes:

> * David Kastrup:
>
>> Now the problem we encounter is that if some structure A points to B and
>> some structure B points to A and both A and B are placed into
>> finalization, then the finalization of A will delete the associated C++
>> structure.  If now a mark pass is allowed through B, it will try to
>> access the deleted C++ structure of A.
>>
>> Topological ordering will not do the trick since cyclical references are
>> quite typical (a NoteHead has a reference to the corresponding Stem, a
>> Stem has references to all corresponding NoteHeads).
>
> Have all of them corresponding C++ objects?  Do you want the garbage
> collector to devise a safe deallocation order, or does deallocation
> happen by another mechanism?

No, a "safe deallocation order" is unachievable because of cycles.
Guile has replaced its own allocator with the Boehm GC.  There is a
mismatch in semantics for type hooks registered with
scm_set_smob_free/scm_set_smob_mark which have the following
description:

 -- C Function: void scm_set_smob_free (scm_t_bits tc, size_t (*free)
          (SCM obj))
     This function sets the smob freeing procedure (sometimes referred
     to as a "finalizer") for the smob type specified by the tag TC.  TC
     is the tag returned by ‘scm_make_smob_type’.

     The FREE procedure must deallocate all resources that are directly
     associated with the smob instance OBJ.  It must assume that all
     ‘SCM’ values that it references have already been freed and are
     thus invalid.

     It must also not call any libguile function or macro except
     ‘scm_gc_free’, ‘SCM_SMOB_FLAGS’, ‘SCM_SMOB_DATA’,
     ‘SCM_SMOB_DATA_2’, and ‘SCM_SMOB_DATA_3’.

     The FREE procedure must return 0.

     Note that defining a freeing procedure is not necessary if the
     resources associated with OBJ consists only of memory allocated
     with ‘scm_gc_malloc’ or ‘scm_gc_malloc_pointerless’ because this
     memory is automatically reclaimed by the garbage collector when it
     is no longer needed (*note ‘scm_gc_malloc’: Memory Blocks.).

 -- C Function: void scm_set_smob_mark (scm_t_bits tc, SCM (*mark) (SCM
          obj))
     This function sets the smob marking procedure for the smob type
     specified by the tag TC.  TC is the tag returned by
     ‘scm_make_smob_type’.

     Defining a marking procedure may sometimes be unnecessary because
     large parts of the process’ memory (with the exception of
     ‘scm_gc_malloc_pointerless’ regions, and ‘malloc’- or
     ‘scm_malloc’-allocated memory) are scanned for live pointers(1).

     The MARK procedure must cause ‘scm_gc_mark’ to be called for every
     ‘SCM’ value that is directly referenced by the smob instance OBJ.
     One of these ‘SCM’ values can be returned from the procedure and
     Guile will call ‘scm_gc_mark’ for it.  This can be used to avoid
     deep recursions for smob instances that form a list.

     It must not call any libguile function or macro except
     ‘scm_gc_mark’, ‘SCM_SMOB_FLAGS’, ‘SCM_SMOB_DATA’,
     ‘SCM_SMOB_DATA_2’, and ‘SCM_SMOB_DATA_3’.

Note in particular that scm_set_smob_free states:

     The FREE procedure must deallocate all resources that are directly
     associated with the smob instance OBJ.  It must assume that all
     ‘SCM’ values that it references have already been freed and are
     thus invalid.

In contrast to that, finalizers in Boehm GC work with fully valid
objects.  In particular, mark passes are made through the finalized data
that expect operative data and consequently call for topologically
sorted finalization while scm_set_smob_free may not depend on any valid
pointers for doing its work: it really is much more related to
destructing objects than to finalizing them.

I _think_ that reading gc.h I have found a workable solution: set Java
collection semantics with GC_set_java_finalization (1);

    GC_API GC_ATTR_DEPRECATED int GC_java_finalization;
                            /* Mark objects reachable from finalizable      */
                            /* objects in a separate post-pass.  This makes */
                            /* it a bit safer to use non-topologically-     */
                            /* ordered finalization.  Default value is      */
                            /* determined by JAVA_FINALIZATION macro.       */
                            /* Enables register_finalizer_unreachable to    */
                            /* work correctly.                              */
                            /* The setter and getter are unsynchronized.    */
    GC_API void GC_CALL GC_set_java_finalization(int);
    GC_API int GC_CALL GC_get_java_finalization(void);

And then, during finalization, clear out each finalized object's Guile
type-id so that no more hooks (in particular no hook set with
scm_set_smob_mark) will be called in any separate post-pass and no
dependency on the destructed C++ objects or their memory exists at the
time of the post-pass (not that I understand the post-pass' utility in
the first place but that apparently is what's causing the problems shown
in <URL:http://debbugs.gnu.org/cgi/bugreport.cgi?bug=19883>).

As far as I understand the code, the Guile-2 code base already does set
GC_java_finalization.  So all that should remain would be clearing out
the type identification for data at finalization time.

-- 
David Kastrup
_______________________________________________
bdwgc mailing list
[email protected]
https://lists.opendylan.org/mailman/listinfo/bdwgc