unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* [PATCH] Marking weak alist vectors
@ 2005-11-08 17:22 Ludovic Courtès
  2005-11-08 23:51 ` Marius Vollmer
                   ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-08 17:22 UTC (permalink / raw)


Hi,

The patch below fixes a bug in the garbage collection process of weak
alist vectors.  Without the patch, a value attached to a weak key (or
the opposite) may be freed just _before_ the weak key in question is
freed.

The problem stems from the fact that `scm_i_mark_weak_vector_non_weaks ()'
would not mark a value attached to a weak key if that weak key is
unmarked.  Therefore, both the weak key and its associated value become
unmarked during the very same mark phase.  And since the order in which
they are swept during the subsequent sweep phase is undefined, we have a
problem.

The patch fixes `scm_i_mark_weak_vector_non_weaks ()'.  This way, a
value attached to a weak key will only become unmarked during the mark
phase _after_ the mark phase during which its weak key became unmarked.
You might want to re-read this sentence carefully.  ;-)

I wrote a test case for this which is to be installed as
`test-suite/standalone/test-weaks.c'.  It uses object properties.

This patch has an undesired side effect: it makes `gc.test' fail.  I'll
have to dig into this.  But we probably had better treat this problem
independently given this (using Guile CVS):

  $ guile
  guile> (gc-live-object-stats)
  Segmentation fault (core dumped)

BTW, in `scm_init_properties ()', SCM_PROPERTIES_WHASH was not made
permanent, which looks like an error.

Thanks,
Ludovic.


libguile/ChangeLog

2005-11-08  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* gc-mark.c (scm_mark_all): Removed C++/C99-style comment.

	* properties.c (scm_init_properties): Make SCM_PROPERTIES_WHASH
	a permanent object.
    
	* weaks.c (weak_vectors): Initialize it to `SCM_EOL'.
	(scm_i_mark_weak_vector_non_weaks): When WEAK_KEYS is false and KEY is
	unmarked, mark it recursively.  Likewise, when WEAK_VALUES if false and
	VALUE is unmarked, mark it recursively.
	(scm_i_remove_weaks): Don't only check whether the first pair in ALIST
	is unmarked: check whether either its KEY (when WEAK_KEYS is true) or
	VALUE (when WEAK_VALUES is true) is unmarked.


test-suite/ChangeLog

2005-11-08  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* standalone/Makefile.am (TESTS): Added `test-weaks'.
	(check_PROGRAMS): Likewise.
    
	* standalone/test-weaks.c: New file.


\f
--- orig/libguile/gc-mark.c
+++ mod/libguile/gc-mark.c
@@ -74,7 +74,7 @@
 
   scm_i_init_weak_vectors_for_gc ();
   scm_i_init_guardians_for_gc ();
-  
+
   scm_i_clear_mark_space ();
   
   /* Mark every thread's stack and registers */
@@ -138,12 +138,9 @@
       break;
     }
 
-  //fprintf (stderr, "%d loops\n", loops);
-
-  /* Remove all unmarked entries from the weak vectors.
-   */
+  /* Remove all unmarked entries from the weak vectors.  */
   scm_i_remove_weaks_from_weak_vectors ();
-  
+
   /* Bring hashtables upto date.
    */
   scm_i_scan_weak_hashtables ();


--- orig/libguile/properties.c
+++ mod/libguile/properties.c
@@ -125,6 +125,7 @@
 scm_init_properties ()
 {
   scm_properties_whash = scm_make_weak_key_hash_table (SCM_UNDEFINED);
+  scm_properties_whash = scm_permanent_object (scm_properties_whash);
 #include "libguile/properties.x"
 }
 

--- orig/libguile/weaks.c
+++ mod/libguile/weaks.c
@@ -205,7 +205,11 @@
 
 #define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
 
-static SCM weak_vectors;
+/* A list of live weak vectors, updated each time a weak vector is marked (in
+   `scm_i_mark_weak_vector ()') and cleared at the beginning of each mark
+   phase (in `scm_mark_all ()' which calls
+   `scm_i_init_weak_vectors_for_gc ()').  */
+static SCM weak_vectors = SCM_EOL;
 
 void
 scm_i_init_weak_vectors_for_gc ()
@@ -264,14 +268,18 @@
 		    {
 		      SCM key = SCM_CAR (elt);
 		      SCM value = SCM_CDR (elt);
-		  
-		      if (!((weak_keys && UNMARKED_CELL_P (key))
-			    || (weak_values && UNMARKED_CELL_P (value))))
+
+		      if ((!weak_keys) && (UNMARKED_CELL_P (key)))
+			scm_gc_mark (key);
+
+		      if ((!weak_values) && (UNMARKED_CELL_P (value)))
+			scm_gc_mark (value);
+
+		      if ((!UNMARKED_CELL_P (key))
+			  && (!UNMARKED_CELL_P (value)))
 			{
-			  /* The item should be kept.  We need to mark it
-			     recursively.
-			  */ 
-			  scm_gc_mark (elt);
+			  /* The whole pair should be kept.  */
+			  SCM_SET_GC_MARK (elt);
 			  again = 1;
 			}
 		    }
@@ -323,6 +331,8 @@
 {
   SCM *ptr = SCM_I_WVECT_GC_WVELTS (w);
   size_t n = SCM_I_WVECT_LENGTH (w);
+  int weak_keys = SCM_IS_WHVEC (w) || SCM_IS_WHVEC_B (w);
+  int weak_values = SCM_IS_WHVEC_V (w) || SCM_IS_WHVEC_B (w);
   size_t i;
 
   if (!SCM_IS_WHVEC_ANY (w))
@@ -343,8 +353,13 @@
 	  alist = *fixup;
 	  while (scm_is_pair (alist) && !SCM_GC_MARK_P (alist))
 	    {
-	      if (UNMARKED_CELL_P (SCM_CAR (alist)))
+	      SCM elt = SCM_CAR (alist);
+	      SCM key = SCM_CAR (elt), value = SCM_CDR (elt);
+
+	      if ((weak_keys && UNMARKED_CELL_P (key))
+		  || (weak_values && UNMARKED_CELL_P (value)))
 		{
+		  /* Remove this pair from ALIST.  */
 		  *fixup = SCM_CDR (alist);
 		  delta++;
 		}


--- orig/test-suite/standalone/Makefile.am
+++ mod/test-suite/standalone/Makefile.am
@@ -74,6 +74,13 @@
 check_PROGRAMS += test-conversion
 TESTS += test-conversion
 
+# test-weaks
+test_weaks_SOURCES = test-weaks.c
+test_weaks_CFLAGS = ${test_cflags}
+test_weaks_LDADD = ${top_builddir}/libguile/libguile.la
+check_PROGRAMS += test-weaks
+TESTS += test-weaks
+
 all-local:
 	cd ${srcdir} && chmod u+x ${check_SCRIPTS}
 


\f
test-weaks.c:

--8<---------------cut here---------------start------------->8---
/* Copyright (C) 2005 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */


/* This test case targets garbage collection of weak hash tables.  It does so
   by using object properties (which are currently implemented using weak
   hash tables) and verifying that properties attached to an object are
   always GC'd _after_ the object itself has been freed.

   In order to do so, `test_weak_gc ()' creates a number of SMOBs.  The C
   structure underlying those SMOBs explicitly maintains a reference counter
   for each instance.  This reference counter is:

   1. incremented each time a SMOB is attached to another SMOB (by means of
      object properties);

   2. decremented each time a SMOB that was attached to another SMOB is
      freed.  For instance if A is attached to B, when B is GC'd, A's
      reference counter is decremented.

   The invariant that we check is: any SMOB that is GC'd must have its
   reference count equal to zero.  */


#include "libguile.h"

#include <stdio.h>
#include <assert.h>

/* Number of time a `my-object' SMOB was freed.  */
static unsigned free_count = 0;

static scm_t_bits my_object_type = 0;
static SCM        some_property = SCM_BOOL_F;
static SCM my_object_new_smob_proc = SCM_BOOL_F,
  attach_object_proc = SCM_BOOL_F;

typedef struct my_object
{
  struct my_object *attached_to;

  unsigned ref_count;
  int      freed;
} my_object_t;


static void
my_object_init (my_object_t *obj)
{
  obj->attached_to = NULL;
  obj->ref_count = 0;
  obj->freed = 0;
}

static size_t
my_object_free (SCM obj)
{
  my_object_t *my_object = (my_object_t *)SCM_SMOB_DATA (obj);

  if (my_object->attached_to)
    {
      /* Decrease the reference count of the object MY_OBJECT is attached
	 to.  */
      assert (my_object->attached_to->ref_count > 0);
      my_object->attached_to->ref_count--;
    }

  /* MY_OBJECT must not have been already freed and there must be no pending
     references to it.  */
  assert (!my_object->freed);
  assert (my_object->ref_count == 0);

  my_object->freed = 1;

  free_count++;

  return 0;
}

\f
/* Return a new `my-object' SMOB.  */
static SCM
my_object_new_smob (void)
{
  my_object_t *obj;

  obj = scm_malloc (sizeof (*obj));
  my_object_init (obj);

  SCM_RETURN_NEWSMOB (my_object_type, obj);
}

/* Attach TO_BE_ATTACHED to OBJ.  */
static SCM
attach_object (SCM obj, SCM to_be_attached)
{
  my_object_t *c_obj, *c_to_be_attached;

  assert (SCM_SMOB_PREDICATE (my_object_type, obj));
  assert (SCM_SMOB_PREDICATE (my_object_type, to_be_attached));

  /* TO_BE_ATTACHED is attached as a property of OBJ.  As such, OBJ will get
     GC'd _before_ TO_BE_ATTACHED.  */
  scm_primitive_property_set_x (some_property, obj, to_be_attached);
  c_obj = (my_object_t *)SCM_SMOB_DATA (obj);
  c_to_be_attached = (my_object_t *)SCM_SMOB_DATA (to_be_attached);

  /* Because TO_BE_ATTACHED is to be freed _after_ OBJ, we can increase its
     reference count and it should be zero by the time it is freed.  */
  c_to_be_attached->ref_count++;
  c_obj->attached_to = c_to_be_attached;

  return obj;
}

\f
/* Instantiate a number of `my-object' SMOBs, attached some of them together,
   invoke the GC, and wait until all of these SMOBs have been freed.  */
static void
test_weak_gc (void)
{
#define PAIRS 500
  unsigned pair_count, total;

  for (pair_count = 0, total = 0;
       pair_count < PAIRS;
       pair_count++)
    {
      SCM obj, attached;

      obj = my_object_new_smob ();
      attached = my_object_new_smob ();

#if 0
      printf ("%p attached to %p\n",
	      SCM_SMOB_DATA (obj), SCM_SMOB_DATA (attached));
#endif

      attach_object (obj, attached);
      my_object_new_smob ();
      total += 3;

      obj = attached = SCM_BOOL_F;
    }

  while (free_count < total)
    {
      unsigned i;
      scm_gc ();

      for (i = 0; i < 1000; i++)
	scm_cons (SCM_I_MAKINUM (0), SCM_I_MAKINUM (0));
    }
}

\f
int
main (int argc, char *argv[])
{
  scm_init_guile ();

  my_object_type = scm_make_smob_type ("test-object", 0);
  scm_set_smob_free (my_object_type, my_object_free);

  some_property = scm_primitive_make_property (SCM_BOOL_F);

  my_object_new_smob_proc =
    scm_c_make_gsubr ("make-my-object", 0, 0, 0, my_object_new_smob);
  attach_object_proc =
    scm_c_make_gsubr ("attach-object!", 0, 0, 0, attach_object);

  test_weak_gc ();

  return 0;
}
--8<---------------cut here---------------end--------------->8---


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-08 17:22 [PATCH] Marking weak alist vectors Ludovic Courtès
@ 2005-11-08 23:51 ` Marius Vollmer
  2005-11-09  9:03   ` Ludovic Courtès
  2005-11-09 10:28 ` Han-Wen Nienhuys
  2005-11-17 13:21 ` [PATCH] Fixing `gc-live-object-stats' Ludovic Courtès
  2 siblings, 1 reply; 35+ messages in thread
From: Marius Vollmer @ 2005-11-08 23:51 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> The problem stems from the fact that
> `scm_i_mark_weak_vector_non_weaks ()' would not mark a value
> attached to a weak key if that weak key is unmarked.  Therefore,
> both the weak key and its associated value become unmarked during
> the very same mark phase.  And since the order in which they are
> swept during the subsequent sweep phase is undefined, we have a
> problem.

I still have to read your patch carefully, but just from your
description of the problem, I would say that nothing is actually
wrong.  We don't make any guarantees about the sequence in which
objects are 'freed' that are unreachable.

This should only matter for smobs with their user written FREE
procedures.  The manual says:

     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'.

No?

>   $ guile
>   guile> (gc-live-object-stats)
>   Segmentation fault (core dumped)
>
> BTW, in `scm_init_properties ()', SCM_PROPERTIES_WHASH was not made
> permanent, which looks like an error.

I'll look at these two issues first.

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-08 23:51 ` Marius Vollmer
@ 2005-11-09  9:03   ` Ludovic Courtès
  2005-12-06 23:55     ` Marius Vollmer
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-09  9:03 UTC (permalink / raw)
  Cc: guile-devel

Hi Marius,

Marius Vollmer <mvo@zagadka.de> writes:

> I still have to read your patch carefully, but just from your
> description of the problem, I would say that nothing is actually
> wrong.  We don't make any guarantees about the sequence in which
> objects are 'freed' that are unreachable.
>
> This should only matter for smobs with their user written FREE
> procedures.  The manual says:

[About SMOBs' being freed]

The relevant part of the manual with respect to this issue is the
following (in node `Weak References'):

     Weak references let you attach bookkeeping information to data so
  that the additional information automatically disappears when the
  original data is no longer in use and gets garbage collected. In a weak
  key hash, the hash entry for that key disappears as soon as the key is
  no longer referenced from anywhere else.

Likewise (in `Object Properties'):

     Internally, object properties are implemented using a weak key hash
  table.  This means that, as long as a Scheme value with property values
  is protected from garbage collection, its property values are also
  protected.  When the Scheme value is collected, its entry in the
  property table is removed and so the (ex-) property values are no longer
  protected by the table.

IOW, property values are to be "collected" _after_ the Scheme value to
which they are attached has been collected.  Admittedly, the manual is
quite fuzzy as to whether "after" means "during the next round of
mark/sweep" or "maybe during the same round of mark/sweep but at least
property values are swept after the Scheme value has been swept".

In any case, if a value P is attached as a property of some object O,
then O should be "collected" _before_ P.  In practice, if both O and P
are SMOBs, then this means that O should be "freed" before P.

Please, have a look at the `test-weaks.c' file I posted.  It attaches
SMOBs as properties of other SMOBs.  And it's maybe clearer than long
explanations.  ;-)

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-08 17:22 [PATCH] Marking weak alist vectors Ludovic Courtès
  2005-11-08 23:51 ` Marius Vollmer
@ 2005-11-09 10:28 ` Han-Wen Nienhuys
  2005-11-09 16:28   ` Ludovic Courtès
  2005-11-17 13:21 ` [PATCH] Fixing `gc-live-object-stats' Ludovic Courtès
  2 siblings, 1 reply; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-09 10:28 UTC (permalink / raw)


Ludovic Courtès wrote:
> The patch fixes `scm_i_mark_weak_vector_non_weaks ()'.  This way, a
> value attached to a weak key will only become unmarked during the mark
> phase _after_ the mark phase during which its weak key became unmarked.
> You might want to re-read this sentence carefully.  ;-)

Does your patch solve the problem that cyclical structures (values that 
point back to keys) should also be GC-ed?

> /* Attach TO_BE_ATTACHED to OBJ.  */
> static SCM
> attach_object (SCM obj, SCM to_be_attached)
> {
>   my_object_t *c_obj, *c_to_be_attached;
> 
>   assert (SCM_SMOB_PREDICATE (my_object_type, obj));
>   assert (SCM_SMOB_PREDICATE (my_object_type, to_be_attached));
> 
>   /* TO_BE_ATTACHED is attached as a property of OBJ.  As such, OBJ will get
>      GC'd _before_ TO_BE_ATTACHED.  */
>   scm_primitive_property_set_x (some_property, obj, to_be_attached);

Why are you storing SCM references as properties?  It's more efficient 
   both in time and space to use a SCM member in my_object_t.



-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09 10:28 ` Han-Wen Nienhuys
@ 2005-11-09 16:28   ` Ludovic Courtès
  2005-11-09 18:36     ` Han-Wen Nienhuys
                       ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-09 16:28 UTC (permalink / raw)
  Cc: guile-devel

Han-Wen Nienhuys <hanwen@xs4all.nl> writes:

> Does your patch solve the problem that cyclical structures (values that 
> point back to keys) should also be GC-ed?

I guess you're talking about cyclical structures in doubly-weak alist
vectors.  If so, it apparently does since if both WEAK_VALUES and
WEAK_KEYS are false in both functions, then neither the key nor the
value will ever be marked by those functions.

Actually, I've spent some time re-reviewing this patch and I'm having
starting to have a headache.  But anyway, here are a few thoughts.

1.  The tests in `weaks.test' are broken in several ways, not only
    because "we have no way of knowing for certain that the object is
    really dead" as stated there.

    * First, they assume that

        (begin
          (hashq-set! h "a string" 123)
          (hashq-ref  h "a string))

      returns true.  This is wrong since `hashq-ref' uses `eq?' to
      compare keys, and `(eq? "a string" "a string")' returns #f.
      Instead, it could use `hash-map->list'.

    * Second, it should perform a `(read-disable 'positions)' since
      source properties rely on a weakly-keyed hash table where keys are
      source expressions.

2.  The C test I submitted, unlike `weaks.test', can *reliably*
    determine whether an object was swept.  However, it is clearly not
    as complete as `weaks.test'.

3.  Given the level of non-determinism I've been able to observe, I'm
    afraid leaks are causing us difficulties.  For instance, while
    testing weakly-key alist vectors "by hand" in a REPL, it occurred to
    me that the weak-key pair would reliably die, *unless* the hash
    table was written (I mean using `write'):

      guile> (define h (make-doubly-weak-alist-vector 12))
      guile> (hashq-set! h "sdf" "paf")
      guile> (hashq-set! h "hello" "world")
      guile> (gc)
      guile> h
      #w(() () () () () () () () () () () ())

    The same but print H before calling `gc':

      guile> (hashq-set! h "sdf" "paf")
      guile> (hashq-set! h "hello" "world")
      guile> h
      #w((("hello" . "world") ("sdf" . "paf")) () () () () () () () () () () ())
      guile> (gc)
      guile> (gc)
      guile> (gc)
      guile> h
      #w((("hello" . "world") ("sdf" . "paf")) () () () () () () () () () () ())

4.  Looking a Bruno Haible's paper[0] on this topic, it seems that getting
    it right is, well, pretty hard.  ;-)

> Why are you storing SCM references as properties?  It's more efficient 
>   both in time and space to use a SCM member in my_object_t.

That's the whole point of the test: object properties are used because
they involve weak hash tables.

Thanks,
Ludovic.

[0] http://www.haible.de/bruno/papers/cs/weak/WeakDatastructures-writeup.html


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09 16:28   ` Ludovic Courtès
@ 2005-11-09 18:36     ` Han-Wen Nienhuys
  2005-11-09 21:11     ` Kevin Ryde
  2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
  2 siblings, 0 replies; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-09 18:36 UTC (permalink / raw)
  Cc: guile-devel

Ludovic Courtès wrote:
>>Does your patch solve the problem that cyclical structures (values that 
>>point back to keys) should also be GC-ed?
> 
> 
> I guess you're talking about cyclical structures in doubly-weak alist
> vectors.  If so, it apparently does since if both WEAK_VALUES and
> WEAK_KEYS are false in both functions, then neither the key nor the
> value will ever be marked by those functions.

Actually, I was talking about tables with only weak keys, but strong values.

> 1.  The tests in `weaks.test' are broken in several ways, not only
>     because "we have no way of knowing for certain that the object is
>     really dead" as stated there.

You can detect whether objects died; just generate a lot of them, and 
see what happens to the stats the output of (gc-live-object-stats) after 
a few GCs.

> 3.  Given the level of non-determinism I've been able to observe, I'm
>     afraid leaks are causing us difficulties.  For instance, while
>     testing weakly-key alist vectors "by hand" in a REPL, it occurred to
>     me that the weak-key pair would reliably die, *unless* the hash
>     table was written (I mean using `write'):

Curious; why does this happen?

 > That's the whole point of the test: object properties are used because
 > they involve weak hash tables.

In that case, I'm missing the point completely; why do you need this 
functionality?

-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09 16:28   ` Ludovic Courtès
  2005-11-09 18:36     ` Han-Wen Nienhuys
@ 2005-11-09 21:11     ` Kevin Ryde
  2005-11-09 22:45       ` Marius Vollmer
  2005-11-10  9:47       ` [PATCH] Reference leak in `iprin1 ()' Ludovic Courtès
  2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
  2 siblings, 2 replies; 35+ messages in thread
From: Kevin Ryde @ 2005-11-09 21:11 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>
>     For instance, while
>     testing weakly-key alist vectors "by hand" in a REPL, it occurred to
>     me that the weak-key pair would reliably die, *unless* the hash
>     table was written (I mean using `write'):

I may have struck that or something similar a while back.  Mikael
explained it was a reference held in the print data of the output
port, or something.  Used to detect cyclic structures, or something.
I don't think I understood why such a reference ought to persist once
outside the print, I'd suspect it'd be better if it didn't, if that
could be arranged.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09 21:11     ` Kevin Ryde
@ 2005-11-09 22:45       ` Marius Vollmer
  2005-11-10 12:11         ` Han-Wen Nienhuys
  2005-11-10  9:47       ` [PATCH] Reference leak in `iprin1 ()' Ludovic Courtès
  1 sibling, 1 reply; 35+ messages in thread
From: Marius Vollmer @ 2005-11-09 22:45 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

Kevin Ryde <user42@zip.com.au> writes:

> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>>
>>     For instance, while
>>     testing weakly-key alist vectors "by hand" in a REPL, it occurred to
>>     me that the weak-key pair would reliably die, *unless* the hash
>>     table was written (I mean using `write'):
>
> I may have struck that or something similar a while back.  Mikael
> explained it was a reference held in the print data of the output
> port, or something.  Used to detect cyclic structures, or something.
> I don't think I understood why such a reference ought to persist once
> outside the print, I'd suspect it'd be better if it didn't, if that
> could be arranged.

If I remember right, I have been in exactly this spot as well, but I
couldn't really fix this.  I don't remember why, unfortunately...

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH] Reference leak in `iprin1 ()'
  2005-11-09 21:11     ` Kevin Ryde
  2005-11-09 22:45       ` Marius Vollmer
@ 2005-11-10  9:47       ` Ludovic Courtès
  2005-11-12  9:23         ` Neil Jerram
  1 sibling, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-10  9:47 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

Hi,

Kevin Ryde <user42@zip.com.au> writes:

> I may have struck that or something similar a while back.  Mikael
> explained it was a reference held in the print data of the output
> port, or something.  Used to detect cyclic structures, or something.

Thanks: you put me on the right track!  So this issue is now gone.  :-)

Ludovic.


2005-11-10  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* print.c (EXIT_NESTED_DATA): Before popping from the stack,
	reset the value at its top.  This fixes a reference leak.


--- orig/libguile/print.c
+++ mod/libguile/print.c
@@ -136,7 +136,13 @@
   PUSH_REF(pstate, obj); \
 } while(0)
 
-#define EXIT_NESTED_DATA(pstate) { --pstate->top; }
+#define EXIT_NESTED_DATA(pstate)				\
+do								\
+{								\
+  PSTATE_STACK_SET (pstate, pstate->top, SCM_UNDEFINED);	\
+  --pstate->top;						\
+}								\
+while (0)
 
 SCM scm_print_state_vtable = SCM_BOOL_F;
 static SCM print_state_pool = SCM_EOL;
@@ -144,8 +150,8 @@
 
 #ifdef GUILE_DEBUG /* Used for debugging purposes */
 
-SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0, 
-           (),
+SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0,
+	    (void),
 	    "Return the current-pstate -- the car of the\n"
 	    "@code{print_state_pool}.  @code{current-pstate} is only\n"
 	    "included in @code{--enable-guile-debug} builds.")


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09 22:45       ` Marius Vollmer
@ 2005-11-10 12:11         ` Han-Wen Nienhuys
  0 siblings, 0 replies; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-10 12:11 UTC (permalink / raw)


Marius Vollmer wrote:
>>I may have struck that or something similar a while back.  Mikael
>>explained it was a reference held in the print data of the output
>>port, or something.  Used to detect cyclic structures, or something.
>>I don't think I understood why such a reference ought to persist once
>>outside the print, I'd suspect it'd be better if it didn't, if that
>>could be arranged.
> 
> 
> If I remember right, I have been in exactly this spot as well, but I
> couldn't really fix this.  I don't remember why, unfortunately...

Isn't this a matter of clearing the print state at the end of scm_prin1() ?

-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Reference leak in `iprin1 ()'
  2005-11-10  9:47       ` [PATCH] Reference leak in `iprin1 ()' Ludovic Courtès
@ 2005-11-12  9:23         ` Neil Jerram
  2005-11-14  9:58           ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Neil Jerram @ 2005-11-12  9:23 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> 	* print.c (EXIT_NESTED_DATA): Before popping from the stack,
> 	reset the value at its top.  This fixes a reference leak.

Nice work, but it looks to me that PUSH_REF sets the value of the
(pstate->top)th element _before_ incrementing pstate->top.  So
shouldn't your fix do the decrement first and then set the slot to
undefined?

(Note that your fix will still prevent most reference leaks, just not
the outermost one.  So that's why it may appear to be working
already.)

Also, is the () -> (void) change strictly related?

    Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Reference leak in `iprin1 ()'
  2005-11-12  9:23         ` Neil Jerram
@ 2005-11-14  9:58           ` Ludovic Courtès
  2005-11-16 21:18             ` Neil Jerram
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-14  9:58 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys, guile-devel

Hello,

Neil Jerram <neil@ossau.uklinux.net> writes:

> Nice work, but it looks to me that PUSH_REF sets the value of the
> (pstate->top)th element _before_ incrementing pstate->top.  So
> shouldn't your fix do the decrement first and then set the slot to
> undefined?

Yes, you're perfectly right.

> (Note that your fix will still prevent most reference leaks, just not
> the outermost one.  So that's why it may appear to be working
> already.)

Exactly.

> Also, is the () -> (void) change strictly related?

In fact, it's strictly unrelated, but while we're at it (really `(void)'
was meant here, not `()')...

Below is an updated patch.  I modified `PUSH_REF ()' as well so that it
does PSTATE->TOP++ _after_ using the `PSTATE_STACK_SET ()' macro: this
is safer.  And, well, I couldn't resist the desire to "beautifully
backslashify" `ENTER_NESTED_DATA ()' as well.  :-)  I hope this is not
too much pollution for you.

Thanks,
Ludovic.


2005-11-14  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* print.c (EXIT_NESTED_DATA): Before popping from the stack,
	reset the value at its top.  This fixes a reference leak.
	(PUSH_REF): Perform `pstate->top++' after calling
	`PSTATE_STACK_SET ()' in order to avoid undesired side effects.
	(scm_current_pstate): Replaced `()' by `(void)'.


--- orig/libguile/print.c
+++ mod/libguile/print.c
@@ -112,31 +112,40 @@
  * time complexity (O (depth * N)), The printer code can be
  * rewritten to be O(N).
  */
-#define PUSH_REF(pstate, obj) \
-do { \
-  PSTATE_STACK_SET (pstate, pstate->top++, obj); \
-  if (pstate->top == pstate->ceiling) \
-    grow_ref_stack (pstate); \
+#define PUSH_REF(pstate, obj)			\
+do						\
+{						\
+  PSTATE_STACK_SET (pstate, pstate->top, obj);	\
+  pstate->top++;				\
+  if (pstate->top == pstate->ceiling)		\
+    grow_ref_stack (pstate);			\
 } while(0)
 
-#define ENTER_NESTED_DATA(pstate, obj, label) \
-do { \
-  register unsigned long i; \
-  for (i = 0; i < pstate->top; ++i) \
-    if (scm_is_eq (PSTATE_STACK_REF (pstate, i), (obj))) \
-      goto label; \
-  if (pstate->fancyp) \
-    { \
-      if (pstate->top - pstate->list_offset >= pstate->level) \
-	{ \
-	  scm_putc ('#', port); \
-	  return; \
-	} \
-    } \
-  PUSH_REF(pstate, obj); \
+#define ENTER_NESTED_DATA(pstate, obj, label)			\
+do								\
+{								\
+  register unsigned long i;					\
+  for (i = 0; i < pstate->top; ++i)				\
+    if (scm_is_eq (PSTATE_STACK_REF (pstate, i), (obj)))	\
+      goto label;						\
+  if (pstate->fancyp)						\
+    {								\
+      if (pstate->top - pstate->list_offset >= pstate->level)	\
+	{							\
+	  scm_putc ('#', port);					\
+	  return;						\
+	}							\
+    }								\
+  PUSH_REF(pstate, obj);					\
 } while(0)
 
-#define EXIT_NESTED_DATA(pstate) { --pstate->top; }
+#define EXIT_NESTED_DATA(pstate)				\
+do								\
+{								\
+  --pstate->top;						\
+  PSTATE_STACK_SET (pstate, pstate->top, SCM_UNDEFINED);	\
+}								\
+while (0)
 
 SCM scm_print_state_vtable = SCM_BOOL_F;
 static SCM print_state_pool = SCM_EOL;
@@ -144,8 +153,8 @@
 
 #ifdef GUILE_DEBUG /* Used for debugging purposes */
 
-SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0, 
-           (),
+SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0,
+	    (void),
 	    "Return the current-pstate -- the car of the\n"
 	    "@code{print_state_pool}.  @code{current-pstate} is only\n"
 	    "included in @code{--enable-guile-debug} builds.")




_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Reference leak in `iprin1 ()'
  2005-11-14  9:58           ` Ludovic Courtès
@ 2005-11-16 21:18             ` Neil Jerram
  2005-11-17  9:54               ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Neil Jerram @ 2005-11-16 21:18 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Hello,
>
> Neil Jerram <neil@ossau.uklinux.net> writes:
>
>> Nice work, but it looks to me that PUSH_REF sets the value of the
>> (pstate->top)th element _before_ incrementing pstate->top.  So
>> shouldn't your fix do the decrement first and then set the slot to
>> undefined?
>
> Yes, you're perfectly right.

Thanks.  (It's always better to have other people check and agree!)

>> Also, is the () -> (void) change strictly related?
>
> In fact, it's strictly unrelated, but while we're at it (really `(void)'
> was meant here, not `()')...

Why?  Does () have any symptoms that we should be concerned about?

> Below is an updated patch.  I modified `PUSH_REF ()' as well so that it
> does PSTATE->TOP++ _after_ using the `PSTATE_STACK_SET ()' macro: this
> is safer.

Why is it safer?  My inclination is that it's usually safer to change
less code, other things being equal.

>  And, well, I couldn't resist the desire to "beautifully
> backslashify" `ENTER_NESTED_DATA ()' as well.  :-)  I hope this is not
> too much pollution for you.

No, I'm happy to go along with that one!

Regards,
        Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Reference leak in `iprin1 ()'
  2005-11-16 21:18             ` Neil Jerram
@ 2005-11-17  9:54               ` Ludovic Courtès
  2005-11-17 18:52                 ` Neil Jerram
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-17  9:54 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys, guile-devel

Hi Neil,

Neil Jerram <neil@ossau.uklinux.net> writes:

> Why?  Does () have any symptoms that we should be concerned about?

Well, nothing to be really "concerned" about, mostly consistency.  In a
declaration, `()' has a different meaning than `(void)', but not in the
definition.  According to section 6.7.5.3 or ISO/IEC 9899:1999:

  10 The special case of an unnamed parameter of type void as the only
     item in the list specifies that the function has no parameters.

  [...]

  14 An identifier list declares only the identifiers of the parameters
     of the function. An empty list in a function declarator that is
     part of a definition of that function specifies that the function
     has no parameters. The empty list in a function declarator that is
     not part of a definition of that function specifies that no
     information about the number or types of the parameters is
     supplied.124)

Since, the declarations in `print.h' use `(void)', there's nothing
really serious here.

>> Below is an updated patch.  I modified `PUSH_REF ()' as well so that it
>> does PSTATE->TOP++ _after_ using the `PSTATE_STACK_SET ()' macro: this
>> is safer.
>
> Why is it safer?

Because of the side effects these macros may introduce, as described in
http://gcc.gnu.org/onlinedocs/gcc-4.0.2/cpp/Duplication-of-Side-Effects.html#Duplication-of-Side-Effects .

As far as `PUSH_REF ()' is concerned, without knowing all the
definitions of all the macros it uses, one can't be sure that
PSTATE->TOP won't be incremented more than once.

> My inclination is that it's usually safer to change
> less code, other things being equal.

Agreed.  But that must not prevent us from discussing existing code and
modifying it when we see fit, i.e. when there is improvement to gain.
;-)

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH] Fixing `gc-live-object-stats'
  2005-11-08 17:22 [PATCH] Marking weak alist vectors Ludovic Courtès
  2005-11-08 23:51 ` Marius Vollmer
  2005-11-09 10:28 ` Han-Wen Nienhuys
@ 2005-11-17 13:21 ` Ludovic Courtès
  2005-11-17 14:12   ` Han-Wen Nienhuys
  2005-11-30  8:54   ` Ludovic Courtès
  2 siblings, 2 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-17 13:21 UTC (permalink / raw)


Hi,

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

>   $ guile
>   guile> (gc-live-object-stats)
>   Segmentation fault (core dumped)

The patch below fixes this problem.  Basically, what happens when doing
this is that `scm_i_card_statistics ()' gets called on yet uninitialized
cards.  This is normal because cards are initialized lazily at sweep
time by `scm_i_sweep_some_cards ()' which calls
`scm_i_init_card_freelist ()' if needed.

BTW, looking at the code of `scm_i_sweep_some_cards ()', it looks like
cards can be initialized more than once in the event where THRESHOLD is
reached before NEXT_FREE reached the upper boundary of SEG.  In such a
case, SEG->FIRST_TIME will remain equal to 1, and thus, the next time
this function is called on SEG, `scm_i_init_card_freelist ()' will be
called again for each of SEG's cards.

Did I miss something?

Thanks,
Ludovic.


2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
	This was typically hit when running `gc-live-object-stats' right
	after starting Guile.


--- orig/libguile/gc-card.c
+++ mod/libguile/gc-card.c
@@ -306,6 +306,10 @@
   int span = seg->span;
   int offset = SCM_MAX (SCM_GC_CARD_N_HEADER_CELLS, span);
 
+  if (!bitvec)
+    /* Card P hasn't been initialized yet by `scm_i_init_card_freelist ()'. */
+    return;
+
   for (p += offset; p < end; p += span, offset += span)
     {
       scm_t_bits tag = -1;



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-11-17 13:21 ` [PATCH] Fixing `gc-live-object-stats' Ludovic Courtès
@ 2005-11-17 14:12   ` Han-Wen Nienhuys
  2005-11-30  8:54   ` Ludovic Courtès
  1 sibling, 0 replies; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-17 14:12 UTC (permalink / raw)


Ludovic Courtès wrote:
> BTW, looking at the code of `scm_i_sweep_some_cards ()', it looks like
> cards can be initialized more than once in the event where THRESHOLD is
> reached before NEXT_FREE reached the upper boundary of SEG.  In such a
> case, SEG->FIRST_TIME will remain equal to 1, and thus, the next time
> this function is called on SEG, `scm_i_init_card_freelist ()' will be
> called again for each of SEG's cards.
> 
> Did I miss something?

Yes.  Sweeping always starts with NEXT_FREE, which is incremented for 
each call, so each card from the segment is only passed over once with 
FIRST_TIME. FIRST_TIME refers to the fact that no GC has taken place for 
the segment at hand.

-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Reference leak in `iprin1 ()'
  2005-11-17  9:54               ` Ludovic Courtès
@ 2005-11-17 18:52                 ` Neil Jerram
  0 siblings, 0 replies; 35+ messages in thread
From: Neil Jerram @ 2005-11-17 18:52 UTC (permalink / raw)
  Cc: Han-Wen Nienhuys

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Well, nothing to be really "concerned" about, mostly consistency.  In a
> declaration, `()' has a different meaning than `(void)', but not in the
> definition.  According to section 6.7.5.3 or ISO/IEC 9899:1999:
>
>   10 The special case of an unnamed parameter of type void as the only
>      item in the list specifies that the function has no parameters.
>
>   [...]
>
>   14 An identifier list declares only the identifiers of the parameters
>      of the function. An empty list in a function declarator that is
>      part of a definition of that function specifies that the function
>      has no parameters. The empty list in a function declarator that is
>      not part of a definition of that function specifies that no
>      information about the number or types of the parameters is
>      supplied.124)
>
> Since, the declarations in `print.h' use `(void)', there's nothing
> really serious here.

Thanks.  But in the absence of any actual problem, I think I'll leave
this as is for now.

>>> Below is an updated patch.  I modified `PUSH_REF ()' as well so that it
>>> does PSTATE->TOP++ _after_ using the `PSTATE_STACK_SET ()' macro: this
>>> is safer.
>>
>> Why is it safer?
>
> Because of the side effects these macros may introduce, as described in
> http://gcc.gnu.org/onlinedocs/gcc-4.0.2/cpp/Duplication-of-Side-Effects.html#Duplication-of-Side-Effects .

Yes of course, silly me.  I misread what you said you had done as
changing PSTATE->TOP++ to ++(PSTATE->TOP).  This is definitely a good
change.

This patch is now in CVS.  Thanks for all your effort on this.

     Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, #2
  2005-11-09 16:28   ` Ludovic Courtès
  2005-11-09 18:36     ` Han-Wen Nienhuys
  2005-11-09 21:11     ` Kevin Ryde
@ 2005-11-23 10:19     ` Ludovic Courtès
  2005-11-24  0:59       ` Han-Wen Nienhuys
                         ` (2 more replies)
  2 siblings, 3 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-23 10:19 UTC (permalink / raw)
  Cc: guile-devel

Hi,

Below is an improved version of the patch I originally sent.  The goal
of the original patch (and associated test case) was to ensure that an
object associated to a weak key (resp. a weak value) is GC'd _after_
that key (resp. value).

However, as Han-Wen pointed out earlier in this thread, with the
original patch, cyclical structures within a weak-key (or weak-value)
alist vectors would never become unmarked.  By "cyclical structure", I
mean something like:

  key A is associated to B
  key B is associated to C

Here (assuming a weakly-keyed alist vector), object B is used both as a
key and a value, resulting in a "cyclical structure".

The patch below fixes this.  It also fixes `weaks.test' and adds a test
case for cyclical structures (called "cascading weak keys die").

Looking at `weaks.test' gives an idea of how hard it is to work around
reference "leaks".  Certainly, not all of them are actual leaks, but
some of them may be so.  The funniest example is the `(+ 1 2 3)' call
before calls to `(gc)' to ensure that the C stack no longer holds
references that were used by the *previous* call of `ceval ()'.

Another neat trick is used in "cascading weak keys die": if K2 and K3
are initialized within the init forms of `let', then things won't work
(i.e. references to K2 and K3 are held somewhere).  I suspect this is a
leak in `let' but I was unable to find out what happens exactly.  So
there is room for further debugging.  ;-)

I'd be glad to have feedback about all this.

Thanks,
Ludovic.


libguile/ChangeLog

2005-11-23  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* gc-mark.c (scm_mark_all): Removed C++/C99-style comment.

	* properties.c (scm_init_properties): Make SCM_PROPERTIES_WHASH
	a permanent object.
    
	* weaks.c (weak_vectors): Initialize it to `SCM_EOL'.
	(scm_i_mark_weak_vector_non_weaks): Cosmetic changes.
	(scm_i_remove_weaks): When an element of a pair is weak, add its
	non-weak element to a "mark queue".  Before returning, mark all
	the elements in this queue so that they will only become
	unmarked during the next mark phase.


test-suite/ChangeLog

2005-11-23  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* standalone/Makefile.am (TESTS): Added `test-weaks'.
	(check_PROGRAMS): Likewise.
    
	* standalone/test-weaks.c: New file.

	* tests/weaks.test: Overhauled the test.  Added the "cascading
	weak key dies"test case.


\f
--- orig/libguile/gc-mark.c
+++ mod/libguile/gc-mark.c
@@ -74,7 +74,7 @@
 
   scm_i_init_weak_vectors_for_gc ();
   scm_i_init_guardians_for_gc ();
-  
+
   scm_i_clear_mark_space ();
   
   /* Mark every thread's stack and registers */
@@ -138,12 +138,9 @@
       break;
     }
 
-  //fprintf (stderr, "%d loops\n", loops);
-
-  /* Remove all unmarked entries from the weak vectors.
-   */
+  /* Remove all unmarked entries from the weak vectors.  */
   scm_i_remove_weaks_from_weak_vectors ();
-  
+
   /* Bring hashtables upto date.
    */
   scm_i_scan_weak_hashtables ();


--- orig/libguile/weaks.c
+++ mod/libguile/weaks.c
@@ -205,7 +205,11 @@
 
 #define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
 
-static SCM weak_vectors;
+/* A list of live weak vectors, updated each time a weak vector is marked (in
+   `scm_i_mark_weak_vector ()') and cleared at the beginning of each mark
+   phase (in `scm_mark_all ()' which calls
+   `scm_i_init_weak_vectors_for_gc ()').  */
+static SCM weak_vectors = SCM_EOL;
 
 void
 scm_i_init_weak_vectors_for_gc ()
@@ -264,13 +268,12 @@
 		    {
 		      SCM key = SCM_CAR (elt);
 		      SCM value = SCM_CDR (elt);
-		  
+
 		      if (!((weak_keys && UNMARKED_CELL_P (key))
 			    || (weak_values && UNMARKED_CELL_P (value))))
 			{
-			  /* The item should be kept.  We need to mark it
-			     recursively.
-			  */ 
+			  /* The whole pair should be kept, as well as its
+			     CAR and CDR, recursively.  */
 			  scm_gc_mark (elt);
 			  again = 1;
 			}
@@ -323,6 +326,8 @@
 {
   SCM *ptr = SCM_I_WVECT_GC_WVELTS (w);
   size_t n = SCM_I_WVECT_LENGTH (w);
+  int weak_keys = SCM_IS_WHVEC (w) || SCM_IS_WHVEC_B (w);
+  int weak_values = SCM_IS_WHVEC_V (w) || SCM_IS_WHVEC_B (w);
   size_t i;
 
   if (!SCM_IS_WHVEC_ANY (w))
@@ -334,8 +339,15 @@
   else
     {
       size_t delta = 0;
+      SCM *mark_queue;
+      size_t mark_queue_len = 0;
 
-      for (i = 0; i < n; ++i)
+      if (weak_keys && weak_values)
+	mark_queue = alloca (2 * n * sizeof (*mark_queue));
+      else
+	mark_queue = alloca (n * sizeof (*mark_queue));
+
+      for (i = 0; i < n; i++)
 	{
 	  SCM alist, *fixup;
 
@@ -343,8 +355,20 @@
 	  alist = *fixup;
 	  while (scm_is_pair (alist) && !SCM_GC_MARK_P (alist))
 	    {
-	      if (UNMARKED_CELL_P (SCM_CAR (alist)))
+	      SCM elt = SCM_CAR (alist);
+	      SCM key = SCM_CAR (elt), value = SCM_CDR (elt);
+
+	      if ((weak_keys && UNMARKED_CELL_P (key))
+		  || (weak_values && UNMARKED_CELL_P (value)))
 		{
+		  /* Remove this pair from ALIST.  However, mark its elements
+		     so that they will only become unreachable during the
+		     next mark phase.  */
+		  if (!weak_values)
+		    mark_queue[mark_queue_len++] = value;
+		  if (!weak_keys)
+		    mark_queue[mark_queue_len++] = key;
+
 		  *fixup = SCM_CDR (alist);
 		  delta++;
 		}
@@ -354,11 +378,21 @@
 		  fixup = SCM_CDRLOC (alist);
 		}
 	      alist = *fixup;
+
+	      elt = key = value = SCM_BOOL_F;
 	    }
 	}
+
+      for (i = 0; i < mark_queue_len; i++)
+	{
+	  scm_gc_mark (mark_queue[i]);
+	  mark_queue[i] = SCM_UNSPECIFIED;
+	}
+
 #if 0
       if (delta)
-	fprintf (stderr, "vector %p, delta %d\n", w, delta);
+	fprintf (stderr, "vector %p, delta %d, post-marked %d\n",
+		 w, delta, mark_queue_len);
 #endif
       SCM_I_SET_WVECT_DELTA (w, delta);
     }


--- orig/test-suite/tests/weaks.test
+++ mod/test-suite/tests/weaks.test
@@ -36,7 +36,8 @@
 (use-modules (test-suite lib)
 	     (ice-9 weak-vector))
 
-;;; Creation functions 
+
+;;; Creation functions
 
 
 (with-test-prefix
@@ -105,7 +106,7 @@
 ;;; Normal weak vectors
 (let ((x (make-weak-vector 10 #f))
       (bar "bar"))
-  (with-test-prefix 
+  (with-test-prefix
    "weak-vector"
    (pass-if "lives"
 	    (begin
@@ -123,10 +124,10 @@
 		  (throw 'unresolved))))))
 
  (let ((x (make-weak-key-alist-vector 17))
-      (y (make-weak-value-alist-vector 17))
-      (z (make-doubly-weak-alist-vector 17))
-      (test-key "foo")
-      (test-value "bar"))
+       (y (make-weak-value-alist-vector 17))
+       (z (make-doubly-weak-alist-vector 17))
+       (test-key "foo")
+       (test-value "bar"))
   (with-test-prefix
    "weak-hash"
    (pass-if "lives"
@@ -140,50 +141,71 @@
 		   (hashq-ref y test-key)
 		   (hashq-ref z test-key)
 		   #t)))
+
    (pass-if "weak-key dies"
 	    (begin
-	      (hashq-set! x "this" "is")
-	      (hashq-set! x "a" "test")
-	      (hashq-set! x "of" "the")
-	      (hashq-set! x "emergency" "weak")
-	      (hashq-set! x "key" "hash system")
-	      (gc)
-	      (and 
-	       (or (not (hashq-ref x "this"))
-		   (not (hashq-ref x "a"))
-		   (not (hashq-ref x "of"))
-		   (not (hashq-ref x "emergency"))
-		   (not (hashq-ref x "key")))
-	       (hashq-ref x test-key)
-	       #t)))
+	      ;; We use `string-copy' because the `SCM_IM_BEGIN' structure
+	      ;; holds a reference to the strings that were read so these
+	      ;; strings would not be GC'd until the `begin' expression is.
+	      (hashq-set! x (string-copy "this") "is")
+	      (hashq-set! x (string-copy "a") "test")
+	      (hashq-set! x (string-copy "of") "the")
+	      (hashq-set! x (string-copy "emergency") "weak")
+	      (hashq-set! x (string-copy "key") "hash system")
+
+	      ;; This has the effect of cleaning up the C stack because the
+	      ;; stack space between `ceval ()' and `scm_gc ()' may contain
+	      ;; references to the arguments of the last `hashq-set!' call
+	      ;; above.
+	      (+ 1 2 3)
+	      (gc) (gc) (gc)
+
+	      ;; X must now contain nothing more than the
+	      ;; `(test-key . test-value)' pair.
+	      (equal? (hash-map->list cons x)
+		      (list (cons test-key test-value)))))
+
+   (pass-if "cascading weak keys die"
+	    (let ((h (make-weak-key-alist-vector 17)))
+	      (let ((k2 #f)
+		    (k3 #f))
+		;; Here, K2 and K3 are used both as keys and values.  Garbage
+		;; collection of these two objects is expected to occur in a
+		;; ``cascading'' fashion: K2 becomes unmarked, so K3 becomes
+		;; unmarked.
+		(set! k2 (string-copy "a shared key/value"))
+		(set! k3 (string-copy "another shared key/value"))
+		(hashq-set! h (string-copy "a key") k2)
+		(hashq-set! h k2 k3)
+		(hashq-set! h k3 (string-copy "some value"))
+		(set! k2 #f)
+		(set! k3 #f)
+		(+ 1 2 3))
+
+	      (gc) (gc) (gc)
+	      (null? (hash-map->list cons h))))
 
    (pass-if "weak-value dies"
 	    (begin
-	      (hashq-set! y "this" "is")
-	      (hashq-set! y "a" "test")
-	      (hashq-set! y "of" "the")
-	      (hashq-set! y "emergency" "weak")
-	      (hashq-set! y "value" "hash system")
-	      (gc)
-	      (and (or (not (hashq-ref y "this"))
-		       (not (hashq-ref y "a"))
-		       (not (hashq-ref y "of"))
-		       (not (hashq-ref y "emergency"))
-		       (not (hashq-ref y "value")))
-		   (hashq-ref y test-key)
-		   #t)))
+	      (hashq-set! y "this" (string-copy "is"))
+	      (hashq-set! y "a" (string-copy "test"))
+	      (hashq-set! y "of" (string-copy "the"))
+	      (hashq-set! y "emergency" (string-copy "weak"))
+	      (hashq-set! y "value" (string-copy "hash system"))
+	      (+ 1 2 3)
+	      (gc) (gc) (gc)
+	      (equal? (hash-map->list cons y)
+			   (list (cons test-key test-value)))))
+
    (pass-if "doubly-weak dies"
 	    (begin
-	      (hashq-set! z "this" "is")
-	      (hashq-set! z "a" "test")
-	      (hashq-set! z "of" "the")
-	      (hashq-set! z "emergency" "weak")
-	      (hashq-set! z "all" "hash system")
-	      (gc)
-	      (and (or (not (hashq-ref z "this"))
-		       (not (hashq-ref z "a"))
-		       (not (hashq-ref z "of"))
-		       (not (hashq-ref z "emergency"))
-		       (not (hashq-ref z "all")))
-		   (hashq-ref z test-key)
-		   #t)))))
+	      (hashq-set! z (string-copy "this") (string-copy "is"))
+	      (hashq-set! z (string-copy "a") (string-copy "test"))
+	      (hashq-set! z (string-copy "of") (string-copy "the"))
+	      (hashq-set! z (string-copy "emergency") (string-copy "weak"))
+	      (hashq-set! z (string-copy "all") (string-copy "hash system"))
+	      (+ 1 2 3)
+	      (gc) (gc) (gc)
+	      (equal? (hash-map->list cons z)
+		      (list (cons test-key test-value)))))))
+


--- orig/test-suite/standalone/Makefile.am
+++ mod/test-suite/standalone/Makefile.am
@@ -74,6 +74,13 @@
 check_PROGRAMS += test-conversion
 TESTS += test-conversion
 
+# test-weaks
+test_weaks_SOURCES = test-weaks.c
+test_weaks_CFLAGS = ${test_cflags}
+test_weaks_LDADD = ${top_builddir}/libguile/libguile.la
+check_PROGRAMS += test-weaks
+TESTS += test-weaks
+
 all-local:
 	cd ${srcdir} && chmod u+x ${check_SCRIPTS}
 



\f
New file `test-suite/standalone/test-weaks.c':

/* Copyright (C) 2005 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */


/* This test case targets garbage collection of weak hash tables.  It does so
   by using object properties (which are currently implemented using weak
   hash tables) and verifying that properties attached to an object are
   always GC'd _after_ the object itself has been freed.

   In order to do so, `test_weak_gc ()' creates a number of SMOBs.  The C
   structure underlying those SMOBs explicitly maintains a reference counter
   for each instance.  This reference counter is:

   1. incremented each time a SMOB is attached to another SMOB (by means of
      object properties);

   2. decremented each time a SMOB that was attached to another SMOB is
      freed.  For instance if A is attached to B, when B is GC'd, A's
      reference counter is decremented.

   The invariant that we check is: any SMOB that is GC'd must have its
   reference count equal to zero.  */


#include "libguile.h"

#include <stdio.h>
#include <assert.h>

/* Number of time a `my-object' SMOB was freed.  */
static unsigned free_count = 0;

static scm_t_bits my_object_type = 0;
static SCM        some_property = SCM_BOOL_F;
static SCM my_object_new_smob_proc = SCM_BOOL_F,
  attach_object_proc = SCM_BOOL_F;

typedef struct my_object
{
  struct my_object *attached_to;

  unsigned ref_count;
  int      freed;
} my_object_t;


static void
my_object_init (my_object_t *obj)
{
  obj->attached_to = NULL;
  obj->ref_count = 0;
  obj->freed = 0;
}

static size_t
my_object_free (SCM obj)
{
  my_object_t *my_object = (my_object_t *)SCM_SMOB_DATA (obj);

  if (my_object->attached_to)
    {
      /* Decrease the reference count of the object MY_OBJECT is attached
	 to.  */
      assert (my_object->attached_to->ref_count > 0);
      my_object->attached_to->ref_count--;
    }

  /* MY_OBJECT must not have been already freed and there must be no pending
     references to it.  */
  assert (!my_object->freed);
  assert (my_object->ref_count == 0);

  my_object->freed = 1;

  free_count++;

  return 0;
}

\f
/* Return a new `my-object' SMOB.  */
static SCM
my_object_new_smob (void)
{
  my_object_t *obj;

  obj = scm_malloc (sizeof (*obj));
  my_object_init (obj);

  SCM_RETURN_NEWSMOB (my_object_type, obj);
}

/* Attach TO_BE_ATTACHED to OBJ.  */
static SCM
attach_object (SCM obj, SCM to_be_attached)
{
  my_object_t *c_obj, *c_to_be_attached;

  assert (SCM_SMOB_PREDICATE (my_object_type, obj));
  assert (SCM_SMOB_PREDICATE (my_object_type, to_be_attached));

  /* TO_BE_ATTACHED is attached as a property of OBJ.  As such, OBJ will get
     GC'd _before_ TO_BE_ATTACHED.  */
  scm_primitive_property_set_x (some_property, obj, to_be_attached);
  c_obj = (my_object_t *)SCM_SMOB_DATA (obj);
  c_to_be_attached = (my_object_t *)SCM_SMOB_DATA (to_be_attached);

  /* Because TO_BE_ATTACHED is to be freed _after_ OBJ, we can increase its
     reference count and it should be zero by the time it is freed.  */
  c_to_be_attached->ref_count++;
  c_obj->attached_to = c_to_be_attached;

  return obj;
}

\f
/* Instantiate a number of `my-object' SMOBs, attached some of them together,
   invoke the GC, and wait until all of these SMOBs have been freed.  */
static void
test_weak_gc (void)
{
#define PAIRS 700
  unsigned pair_count, total;

  for (pair_count = 0, total = 0;
       pair_count < PAIRS;
       pair_count++)
    {
      size_t noise, i;
      SCM obj, attached;

      obj = my_object_new_smob ();
      attached = my_object_new_smob ();

#if 0
      printf ("%p attached to %p\n",
	      SCM_SMOB_DATA (obj), SCM_SMOB_DATA (attached));
#endif

      attach_object (obj, attached);
      total += 2;

      obj = attached = SCM_BOOL_F;

      for (i = 0, noise = random () % 10;
	   i < noise;
	   i++)
	{
	  my_object_new_smob ();
	  total++;
	}
    }

  while (free_count < total)
    {
      unsigned i;
      scm_gc ();

      for (i = 0; i < 1000; i++)
	scm_cons (SCM_I_MAKINUM (0), SCM_I_MAKINUM (0));
    }
}

\f
int
main (int argc, char *argv[])
{
  scm_init_guile ();

  my_object_type = scm_make_smob_type ("test-object", 0);
  scm_set_smob_free (my_object_type, my_object_free);

  some_property = scm_primitive_make_property (SCM_BOOL_F);

  my_object_new_smob_proc =
    scm_c_make_gsubr ("make-my-object", 0, 0, 0, my_object_new_smob);
  attach_object_proc =
    scm_c_make_gsubr ("attach-object!", 0, 0, 0, attach_object);

  test_weak_gc ();

  return 0;
}


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, #2
  2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
@ 2005-11-24  0:59       ` Han-Wen Nienhuys
  2005-11-24  9:01         ` Ludovic Courtès
  2005-11-26  0:49       ` Kevin Ryde
  2006-01-09 14:51       ` [PATCH] Marking weak alist vectors, epilogue Ludovic Courtès
  2 siblings, 1 reply; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-24  0:59 UTC (permalink / raw)
  Cc: guile-devel

Ludovic Courtès wrote:
> Hi,
> 
> Below is an improved version of the patch I originally sent.  The goal
> of the original patch (and associated test case) was to ensure that an
> object associated to a weak key (resp. a weak value) is GC'd _after_
> that key (resp. value).
> 
> However, as Han-Wen pointed out earlier in this thread, with the
> original patch, cyclical structures within a weak-key (or weak-value)
> alist vectors would never become unmarked.  By "cyclical structure", I
> mean something like:
> 
>   key A is associated to B
>   key B is associated to C
> 
> Here (assuming a weakly-keyed alist vector), object B is used both as a
> key and a value, resulting in a "cyclical structure".

No, this is not cyclic. The problem is a situation where key A is 
associated to key A, eg.

   (define x (cons 1 2))
   (set-cdr! x s)
   (weak-key-hash-table-set! table s x)

x points back to s, and s is a key in the table.

(I haven't looked at how your patch deals with this, just clarifying 
your text)

-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, #2
  2005-11-24  0:59       ` Han-Wen Nienhuys
@ 2005-11-24  9:01         ` Ludovic Courtès
  0 siblings, 0 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-24  9:01 UTC (permalink / raw)
  Cc: guile-devel

Hi,

Han-Wen Nienhuys <hanwen@xs4all.nl> writes:

> No, this is not cyclic. The problem is a situation where key A is 
> associated to key A, eg.
>
>   (define x (cons 1 2))
>   (set-cdr! x s)
>   (weak-key-hash-table-set! table s x)
>
> x points back to s, and s is a key in the table.

Ah, alright.  Well yes, the new patch does handle it, the previous one
didn't.  The issue at hand was similar to the one which showed up in my
"cascading weak keys" example.

  $ ../pre-inst-guile --no-debug
  guile> (use-modules (ice-9 weak-vector))
  guile> (define t (make-weak-key-alist-vector 12))
  guile> (define s (string-copy "chbouib"))
  guile> (hashq-set! t s (cons s 1))
  ("chbouib" . 1)
  guile> (set! s #f)
  guile> (gc)  
  guile> t
  #wh(() () () () () () () () () () () ())

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, #2
  2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
  2005-11-24  0:59       ` Han-Wen Nienhuys
@ 2005-11-26  0:49       ` Kevin Ryde
  2006-01-09 14:51       ` [PATCH] Marking weak alist vectors, epilogue Ludovic Courtès
  2 siblings, 0 replies; 35+ messages in thread
From: Kevin Ryde @ 2005-11-26  0:49 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>
> 	* gc-mark.c (scm_mark_all): Removed C++/C99-style comment.

Thanks, I made that change.  But it's much easier if you post nitpicks
separate from new stuff.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-11-17 13:21 ` [PATCH] Fixing `gc-live-object-stats' Ludovic Courtès
  2005-11-17 14:12   ` Han-Wen Nienhuys
@ 2005-11-30  8:54   ` Ludovic Courtès
  2005-11-30 23:45     ` Han-Wen Nienhuys
  1 sibling, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-11-30  8:54 UTC (permalink / raw)


Hi,

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> 2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>
>
> 	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
> 	This was typically hit when running `gc-live-object-stats' right
> 	after starting Guile.

Can this be applied?

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-11-30  8:54   ` Ludovic Courtès
@ 2005-11-30 23:45     ` Han-Wen Nienhuys
  2005-12-03 19:31       ` Neil Jerram
  0 siblings, 1 reply; 35+ messages in thread
From: Han-Wen Nienhuys @ 2005-11-30 23:45 UTC (permalink / raw)


Ludovic Courtès wrote:
> Hi,
> 
> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
> 
> 
>>2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>
>>
>>	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
>>	This was typically hit when running `gc-live-object-stats' right
>>	after starting Guile.
> 
> 
> Can this be applied?

Yes, I think so.

-- 
  Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-11-30 23:45     ` Han-Wen Nienhuys
@ 2005-12-03 19:31       ` Neil Jerram
  2005-12-05  8:50         ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Neil Jerram @ 2005-12-03 19:31 UTC (permalink / raw)
  Cc: guile-devel

Han-Wen Nienhuys <hanwen@xs4all.nl> writes:

> Ludovic Courtès wrote:
>> Hi,
>> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>>
>>>2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>
>>>
>>>	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
>>>	This was typically hit when running `gc-live-object-stats' right
>>>	after starting Guile.
>> Can this be applied?
>
> Yes, I think so.

Ludovic, if you can post the patch again, I'll commit it.

     Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-12-03 19:31       ` Neil Jerram
@ 2005-12-05  8:50         ` Ludovic Courtès
  2005-12-06 19:14           ` Neil Jerram
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-12-05  8:50 UTC (permalink / raw)
  Cc: guile-devel

Hi,

Neil Jerram <neil@ossau.uklinux.net> writes:

> Ludovic, if you can post the patch again, I'll commit it.

Thanks Neil, I appreciate your involvement.

Ludovic.


2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>

	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
	This was typically hit when running `gc-live-object-stats' right
	after starting Guile.


--- orig/libguile/gc-card.c
+++ mod/libguile/gc-card.c
@@ -306,6 +306,10 @@
   int span = seg->span;
   int offset = SCM_MAX (SCM_GC_CARD_N_HEADER_CELLS, span);
 
+  if (!bitvec)
+    /* Card P hasn't been initialized yet by `scm_i_init_card_freelist ()'. */
+    return;
+
   for (p += offset; p < end; p += span, offset += span)
     {
       scm_t_bits tag = -1;



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Fixing `gc-live-object-stats'
  2005-12-05  8:50         ` Ludovic Courtès
@ 2005-12-06 19:14           ` Neil Jerram
  0 siblings, 0 replies; 35+ messages in thread
From: Neil Jerram @ 2005-12-06 19:14 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> 2005-11-17  Ludovic Courtès  <ludovic.courtes@laas.fr>
>
> 	* gc-card.c (scm_i_card_statistics): Return if BITVEC is NULL.
> 	This was typically hit when running `gc-live-object-stats' right
> 	after starting Guile.

This is in CVS now.

     Neil



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-11-09  9:03   ` Ludovic Courtès
@ 2005-12-06 23:55     ` Marius Vollmer
  2005-12-07 10:33       ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Marius Vollmer @ 2005-12-06 23:55 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> IOW, property values are to be "collected" _after_ the Scheme value to
> which they are attached has been collected.

The manual talks from the perspective of a Scheme program here.  For a
Scheme program, the important information is that some object is not
collected _before_ some condition is true.  Thus, the wording in the
manual should be read as 'not before' instead of as 'strictly after'.

Maybe we should change the manual accordingly.

C code can observe the GC doing its thing (via the smob free
functions).  In order not to overconstrain the implementation of the
GC (which is pretty constrained already anyway), we have the
additional rule that there is no guarantee about the order in which
objects are swept during the sweep phase.

Why do you need this guarantee?

(If you really need this feature, I will start talking you into using
guardians to enforce some order on finalization.)

> In any case, if a value P is attached as a property of some object O,
> then O should be "collected" _before_ P.

Why? Isn't it enough to say 'P is not collected before O is unreachable'?

GC's usually don't make any guarantee about what happens with an
object it has become unreachable.  For example, once O and P become
unreachable, it would be totally acceptable for a GC to collect P but
to never get around to actually collect O.  (The GC could be
generational: O could be in the oldest generation and P in the
youngest.  The youngest generation might be collected every MiB of
allocated memory, but the oldest generation might only be collected on
weekends and during national holidays. :)

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-12-06 23:55     ` Marius Vollmer
@ 2005-12-07 10:33       ` Ludovic Courtès
  2005-12-13 23:45         ` Marius Vollmer
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2005-12-07 10:33 UTC (permalink / raw)
  Cc: guile-devel

Marius Vollmer <mvo@zagadka.de> writes:

> C code can observe the GC doing its thing (via the smob free
> functions).  In order not to overconstrain the implementation of the
> GC (which is pretty constrained already anyway), we have the
> additional rule that there is no guarantee about the order in which
> objects are swept during the sweep phase.

Relaxing such constraints may work for Scheme objects that are entirely
under Guile's control, but it certainly isn't acceptable when SMOBs come
into play: SMOBs _will_ from time to time be victims of this, resulting
in double-frees and the likes.

SMOBs shouldn't ever have the impression that a value attached to a weak
key is freed _before_ that weak key.  From the SMOB programmer viewpoint
that is really an error, no matter if it's "before but during the same
phase" or not.

> Why do you need this guarantee?

For a concrete example, you may want to look at `test-weaks.c' which I
posted when this discussion started, i.e. a long time ago.  ;-)

For a more general example where this is an issue:
http://lists.nongnu.org/archive/html/g-wrap-dev/2005-09/msg00006.html
(see at the bottom).

> (If you really need this feature, I will start talking you into using
> guardians to enforce some order on finalization.)

I hadn't look into this so far, and indeed, that looks interesting!
That's a nice reflexive approach to GC.

I don't think it solves my very problem though.

>> In any case, if a value P is attached as a property of some object O,
>> then O should be "collected" _before_ P.
>
> Why? Isn't it enough to say 'P is not collected before O is unreachable'?

If user-definable objects (SMOBs) didn't exist, that would be perfectly
fine.  But the mere existence of SMOBs prevents such optimizations.

Why would we like to retain such sloppy semantics which are problematic
to the C (SMOB) programmer?

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-12-07 10:33       ` Ludovic Courtès
@ 2005-12-13 23:45         ` Marius Vollmer
  2005-12-14  9:30           ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Marius Vollmer @ 2005-12-13 23:45 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Marius Vollmer <mvo@zagadka.de> writes:
>
>> C code can observe the GC doing its thing (via the smob free
>> functions).  In order not to overconstrain the implementation of the
>> GC (which is pretty constrained already anyway), we have the
>> additional rule that there is no guarantee about the order in which
>> objects are swept during the sweep phase.
>
> Relaxing such constraints may work for Scheme objects that are entirely
> under Guile's control, but it certainly isn't acceptable when SMOBs come
> into play: SMOBs _will_ from time to time be victims of this, resulting
> in double-frees and the likes.

Yes, SMOBs could profit from a guarantee about the order of
finalization, but I don't think that guarantee can be implemented
cheaply enough.  (Or can it?)

So, my current point of view is that smobs have to suffer in order to
make live easier for the GC.

> SMOBs shouldn't ever have the impression that a value attached to a weak
> key is freed _before_ that weak key.

This would not only apply to weak keys etc, but to all kinds of
references, right?  I.e., all SCM values belonging to a smob would be
guaranteed to be valid when the free function for that smob is called.
Thus, we are not only talking about your patch for weak hash tables,
but about big changes to the GC in general, right?

>> Why do you need this guarantee?
>
> For a concrete example, you may want to look at `test-weaks.c' which I
> posted when this discussion started, i.e. a long time ago.  ;-)

Heh, that is not an example, that is a test case. :-)

> For a more general example where this is an issue:
> http://lists.nongnu.org/archive/html/g-wrap-dev/2005-09/msg00006.html
> (see at the bottom).

(I only read the article you linked to directly, not the thread.)

So you have a C API that requires a certain order for freeing objects
and that order needs to be honored by the smobs that wrap the objects,
right?

My immediate reaction is to suggest to fix this by layering something
on top of the C API, such as the reference counting that you mention
in your article.  (Refcounting is acceptable since cycles must be
prevented anyway.)

I agree that it would be nice to have Guile help with that, if only by
providing proven example code.

>> (If you really need this feature, I will start talking you into using
>> guardians to enforce some order on finalization.)
>
> I hadn't look into this so far, and indeed, that looks interesting!
> That's a nice reflexive approach to GC.
>
> I don't think it solves my very problem though.

Yeah, you might be right...

> Why would we like to retain such sloppy semantics which are problematic
> to the C (SMOB) programmer?

Because we would need to change the GC significantly... that's the
only reason I can think of.

But note that the thing we are talking about now is very different
from what you started out with: now we are not talking about in which
run of the GC a value belonging to a weak key is collected (i.e., the
key in run n, the value in run n+1), but in what order objects are
finalized within a single run of the GC (if I am not confused now).

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors
  2005-12-13 23:45         ` Marius Vollmer
@ 2005-12-14  9:30           ` Ludovic Courtès
  0 siblings, 0 replies; 35+ messages in thread
From: Ludovic Courtès @ 2005-12-14  9:30 UTC (permalink / raw)
  Cc: guile-devel

Hi Marius,

Marius Vollmer <mvo@zagadka.de> writes:

> Yes, SMOBs could profit from a guarantee about the order of
> finalization, but I don't think that guarantee can be implemented
> cheaply enough.  (Or can it?)
>
> So, my current point of view is that smobs have to suffer in order to
> make live easier for the GC.

IMO, it's not just a fancy improvement for SMOBs but a requirement.
That is, if we cannot provide this guarantee, then we have to either
completely remove SMOBs (hmm, not sure this is a good idea ;-)) or
disallow their use in weak vectors.  Again, for the SMOB programmer
viewpoint, the order of "finalization" is something that should be taken
seriously.

>> SMOBs shouldn't ever have the impression that a value attached to a weak
>> key is freed _before_ that weak key.
>
> This would not only apply to weak keys etc, but to all kinds of
> references, right?  I.e., all SCM values belonging to a smob would be
> guaranteed to be valid when the free function for that smob is called.
> Thus, we are not only talking about your patch for weak hash tables,
> but about big changes to the GC in general, right?

Well, maybe you're extrapolating too much.  ;-)

Of course, this issue is very similar to the one I raised initially.
However, a SMOB that embeds SCM values knows that it is responsible for
marking them.  Therefore, when a SMOB is freed (because it hasn't been
marked), it knows that the SCM values under its control may have
remained unmarked as well and may have been GC'd.

So I think the semantics here are different: you cannot reasonably
expect SCM values embedded within a SMOB to be freed after the SMOB.

> Heh, that is not an example, that is a test case. :-)

Yeah, but it's pretty close to an example.  ;-)

>> For a more general example where this is an issue:
>> http://lists.nongnu.org/archive/html/g-wrap-dev/2005-09/msg00006.html
>> (see at the bottom).
>
> (I only read the article you linked to directly, not the thread.)
>
> So you have a C API that requires a certain order for freeing objects
> and that order needs to be honored by the smobs that wrap the objects,
> right?

Right.

> My immediate reaction is to suggest to fix this by layering something
> on top of the C API, such as the reference counting that you mention
> in your article.  (Refcounting is acceptable since cycles must be
> prevented anyway.)

Sure, reference counting is the usual way to solve that problem (for
instance, that's how glib, gnet, etc. solve this).  But I was convinced
that using object properties would achieve the same goal much more
easily.

> But note that the thing we are talking about now is very different
> from what you started out with: now we are not talking about in which
> run of the GC a value belonging to a weak key is collected (i.e., the
> key in run n, the value in run n+1), but in what order objects are
> finalized within a single run of the GC (if I am not confused now).

As stated earlier, I think your attempt to generalize the problem is
unfruitful.  ;-)  I.e., IMO, the general issue of the order of
finalization within a single run of the GC isn't an actual issue.
We can really tackle the first problem (i.e., "in which run a value
belonging to the GC is collected") without having to worry about the
order of finalization in general.

Furthermore, I think the patch shows that the first issue can be fixed
without introducing a significant performance cost.  The second version
of the patch introduces _some_ overhead in `scm_i_remove_weaks ()',
because it needs to iterate on MARK_QUEUE, but that doesn't seem to be
too much of a burden.

In [0], this issue is not even mentioned.  In fact, I believe it is
quite specific to Guile since other Scheme implementations maybe don't
have such a large C API.  Further research suggests that this is also an
issue in languages such as SmallTalk where finalization is visible to
the application [1,2].  The Java specs (almost) clearly state that a
weak reference is only finalized after the object it refers to has
become "weakly reachable" (well, in the end it says nothing about the
finalization of the object itself...) [3].

Thanks,
Ludovic.

[0] http://www.haible.de/bruno/papers/cs/weak/WeakDatastructures-writeup.html
[1] http://mail.python.org/pipermail/python-dev/2003-November/040299.html
[2] http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Overviews/WeakReferencesAndFinalization.htm
[3] http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ref/WeakReference.html


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, epilogue
  2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
  2005-11-24  0:59       ` Han-Wen Nienhuys
  2005-11-26  0:49       ` Kevin Ryde
@ 2006-01-09 14:51       ` Ludovic Courtès
  2006-01-09 19:29         ` Neil Jerram
  2 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2006-01-09 14:51 UTC (permalink / raw)
  Cc: guile-devel

Hi all,

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Below is an improved version of the patch I originally sent.  The goal
> of the original patch (and associated test case) was to ensure that an
> object associated to a weak key (resp. a weak value) is GC'd _after_
> that key (resp. value).
>
> However, as Han-Wen pointed out earlier in this thread, with the
> original patch, cyclical structures within a weak-key (or weak-value)
> alist vectors would never become unmarked.

I'm afraid no one cares but this second patch is somewhat broken, too,
in that its delayed marking phase (iterating through MARK_QUEUE) is "not
deferred enough".  Iterating though MARK_QUEUE should be done after
_all_ weak vectors have been traversed in `scm_i_remove_weaks ()'.

Implementing it would require to know the total number of weak vectors
so that, at the beginning of `scm_i_remove_weaks ()' we can allocate (on
the stack) a vector of such "mark queues" and only iterate through it
before exiting.

I'm not going to do it I guess (i) because it is getting complicated and
(ii) well, because apparently no one feels concerned by the issue I was
trying to solve, so maybe it's just unimportant after all.  ;-)

Conclusions:

1. If we're not going to solve this problem, at least it should be very
   well documented.

2. The test case currently in CVS is broken and could be either removed
   or replaced by something along the lines of what I posted earlier (I
   mean `weaks.test').

3. I had posted the tiny fix below which still seems valuable.


Thanks,
Ludovic.


--- orig/libguile/properties.c
+++ mod/libguile/properties.c
@@ -125,6 +125,7 @@
 scm_init_properties ()
 {
   scm_properties_whash = scm_make_weak_key_hash_table (SCM_UNDEFINED);
+  scm_properties_whash = scm_permanent_object (scm_properties_whash);
 #include "libguile/properties.x"
 }
 



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, epilogue
  2006-01-09 14:51       ` [PATCH] Marking weak alist vectors, epilogue Ludovic Courtès
@ 2006-01-09 19:29         ` Neil Jerram
  2006-01-10  8:21           ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Neil Jerram @ 2006-01-09 19:29 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> --- orig/libguile/properties.c
> +++ mod/libguile/properties.c
> @@ -125,6 +125,7 @@
>  scm_init_properties ()
>  {
>    scm_properties_whash = scm_make_weak_key_hash_table (SCM_UNDEFINED);
> +  scm_properties_whash = scm_permanent_object (scm_properties_whash);
>  #include "libguile/properties.x"
>  }

What is the symptom of the fact that we are currently missing this
line?

(And sorry that I don't know enough to comment on the wider GC
issues.)

Regards,
    Neil 



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, epilogue
  2006-01-09 19:29         ` Neil Jerram
@ 2006-01-10  8:21           ` Ludovic Courtès
  2006-01-10  9:33             ` Neil Jerram
  0 siblings, 1 reply; 35+ messages in thread
From: Ludovic Courtès @ 2006-01-10  8:21 UTC (permalink / raw)
  Cc: guile-devel

Neil Jerram <neil@ossau.uklinux.net> writes:

> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>
>> --- orig/libguile/properties.c
>> +++ mod/libguile/properties.c
>> @@ -125,6 +125,7 @@
>>  scm_init_properties ()
>>  {
>>    scm_properties_whash = scm_make_weak_key_hash_table (SCM_UNDEFINED);
>> +  scm_properties_whash = scm_permanent_object (scm_properties_whash);
>>  #include "libguile/properties.x"
>>  }
>
> What is the symptom of the fact that we are currently missing this
> line?

You mean: does it actually yield a fault at run-time?  Well, it doesn't
/seem/ to (i.e., I haven't identified any reproducible "bug" caused by
this), but I think it /could/.  ;-)

All other such global variables are, understandably, explicitly made
non-collectable.  For instance, in `srcprop.c', SCM_SOURCE_WHASH is
non-collectable because it is bound to a top-level binding.  In
`symbols.c', the SYMBOLS weak hash table is made permanent via a call to
`scm_permanent_object ()'.

Thanks,
Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, epilogue
  2006-01-10  8:21           ` Ludovic Courtès
@ 2006-01-10  9:33             ` Neil Jerram
  2006-01-10 15:43               ` Ludovic Courtès
  0 siblings, 1 reply; 35+ messages in thread
From: Neil Jerram @ 2006-01-10  9:33 UTC (permalink / raw)


ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Neil Jerram <neil@ossau.uklinux.net> writes:
>
>> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>>
>>> +  scm_properties_whash = scm_permanent_object (scm_properties_whash);
>>
>> What is the symptom of the fact that we are currently missing this
>> line?
>
> You mean: does it actually yield a fault at run-time?  Well, it doesn't
> /seem/ to (i.e., I haven't identified any reproducible "bug" caused by
> this), but I think it /could/.  ;-)
>
> All other such global variables are, understandably, explicitly made
> non-collectable.  For instance, in `srcprop.c', SCM_SOURCE_WHASH is
> non-collectable because it is bound to a top-level binding.  In
> `symbols.c', the SYMBOLS weak hash table is made permanent via a call to
> `scm_permanent_object ()'.

I just looked into this.  scm_properties_whash is actually OK, because
it is defined like this in root.h:

#define scm_properties_whash scm_sys_protects[10]

and the elements of scm_sys_protects are handled specially in
gc-mark.c.

Regards,
    Neil 



_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH] Marking weak alist vectors, epilogue
  2006-01-10  9:33             ` Neil Jerram
@ 2006-01-10 15:43               ` Ludovic Courtès
  0 siblings, 0 replies; 35+ messages in thread
From: Ludovic Courtès @ 2006-01-10 15:43 UTC (permalink / raw)
  Cc: guile-devel

Neil Jerram <neil@ossau.uklinux.net> writes:

> I just looked into this.  scm_properties_whash is actually OK, because
> it is defined like this in root.h:
>
> #define scm_properties_whash scm_sys_protects[10]
>
> and the elements of scm_sys_protects are handled specially in
> gc-mark.c.

Ok, I had completely overlooked this other way of making objects
non-collectable.

Thanks!

Ludovic.


_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel


^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2006-01-10 15:43 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-11-08 17:22 [PATCH] Marking weak alist vectors Ludovic Courtès
2005-11-08 23:51 ` Marius Vollmer
2005-11-09  9:03   ` Ludovic Courtès
2005-12-06 23:55     ` Marius Vollmer
2005-12-07 10:33       ` Ludovic Courtès
2005-12-13 23:45         ` Marius Vollmer
2005-12-14  9:30           ` Ludovic Courtès
2005-11-09 10:28 ` Han-Wen Nienhuys
2005-11-09 16:28   ` Ludovic Courtès
2005-11-09 18:36     ` Han-Wen Nienhuys
2005-11-09 21:11     ` Kevin Ryde
2005-11-09 22:45       ` Marius Vollmer
2005-11-10 12:11         ` Han-Wen Nienhuys
2005-11-10  9:47       ` [PATCH] Reference leak in `iprin1 ()' Ludovic Courtès
2005-11-12  9:23         ` Neil Jerram
2005-11-14  9:58           ` Ludovic Courtès
2005-11-16 21:18             ` Neil Jerram
2005-11-17  9:54               ` Ludovic Courtès
2005-11-17 18:52                 ` Neil Jerram
2005-11-23 10:19     ` [PATCH] Marking weak alist vectors, #2 Ludovic Courtès
2005-11-24  0:59       ` Han-Wen Nienhuys
2005-11-24  9:01         ` Ludovic Courtès
2005-11-26  0:49       ` Kevin Ryde
2006-01-09 14:51       ` [PATCH] Marking weak alist vectors, epilogue Ludovic Courtès
2006-01-09 19:29         ` Neil Jerram
2006-01-10  8:21           ` Ludovic Courtès
2006-01-10  9:33             ` Neil Jerram
2006-01-10 15:43               ` Ludovic Courtès
2005-11-17 13:21 ` [PATCH] Fixing `gc-live-object-stats' Ludovic Courtès
2005-11-17 14:12   ` Han-Wen Nienhuys
2005-11-30  8:54   ` Ludovic Courtès
2005-11-30 23:45     ` Han-Wen Nienhuys
2005-12-03 19:31       ` Neil Jerram
2005-12-05  8:50         ` Ludovic Courtès
2005-12-06 19:14           ` Neil Jerram

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).