diff --git a/libguile/hashtab.c b/libguile/hashtab.c index 453a6742b..198db4fe7 100644 --- a/libguile/hashtab.c +++ b/libguile/hashtab.c @@ -162,25 +162,40 @@ scm_i_rehash (SCM table, else new_buckets = scm_c_make_vector (new_size, SCM_EOL); + /* When this is a weak hashtable, running the GC might change it. + We need to cope with this while rehashing its elements. We do + this by first installing the new, empty bucket vector and turning + the old bucket vector into a regularily scanned weak vector. + Then we iterate over the elements in the old bucket vector. + While iterating, the elements in LS will not disappear since they + are protected. + */ + + SCM_SET_HASHTABLE_VECTOR (table, new_buckets); + SCM_SET_HASHTABLE_N_ITEMS (table, 0); + if (SCM_HASHTABLE_WEAK_P (table)) + SCM_I_SET_WVECT_TYPE (buckets, (SCM_HASHTABLE_FLAGS (table))); + old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets); for (i = 0; i < old_size; ++i) { - SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle; - while (!scm_is_null (ls)) + SCM ls, cell, handle; + + ls = SCM_SIMPLE_VECTOR_REF (buckets, i); + while (scm_is_pair (ls)) { unsigned long h; - handle = SCM_CAR (ls); + cell = ls; + handle = SCM_CAR (cell); + ls = SCM_CDR (ls); h = hash_fn (SCM_CAR (handle), new_size, closure); if (h >= new_size) scm_out_of_range (func_name, scm_from_ulong (h)); - SCM_SIMPLE_VECTOR_SET - (new_buckets, h, - scm_cons (handle, - SCM_SIMPLE_VECTOR_REF (new_buckets, h))); - ls = SCM_CDR (ls); + SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h)); + SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell); + SCM_HASHTABLE_INCREMENT (table); } } - SCM_SET_HASHTABLE_VECTOR (table, new_buckets); } @@ -475,8 +490,11 @@ scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_ return it; else { - SCM old_bucket = SCM_SIMPLE_VECTOR_REF (buckets, k); - SCM new_bucket = scm_acons (obj, init, old_bucket); + /* We need to take care not to run the GC while linking the new + bucket into its chain. A GC might change a weak hashtable. + */ + SCM new_bucket = scm_acons (obj, init, SCM_EOL); + SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k)); SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket); if (table != buckets) { diff --git a/libguile/vectors.h b/libguile/vectors.h index f846bfd59..74e06d9cd 100644 --- a/libguile/vectors.h +++ b/libguile/vectors.h @@ -95,8 +95,9 @@ SCM_API SCM scm_i_vector_equal_p (SCM x, SCM y); #define SCM_I_WVECT_VELTS SCM_I_VECTOR_ELTS #define SCM_I_WVECT_GC_WVELTS SCM_I_VECTOR_WELTS #define SCM_I_WVECT_TYPE(x) (SCM_CELL_WORD_2 (x)) -#define SCM_I_WVECT_GC_CHAIN(X) (SCM_CELL_OBJECT_3 (X)) -#define SCM_I_SET_WVECT_GC_CHAIN(X, o) (SCM_SET_CELL_OBJECT_3 ((X), (o))) +#define SCM_I_SET_WVECT_TYPE(x, t) (SCM_SET_CELL_WORD_2 ((x),(t))) +#define SCM_I_WVECT_GC_CHAIN(x) (SCM_CELL_OBJECT_3 (x)) +#define SCM_I_SET_WVECT_GC_CHAIN(x, o) (SCM_SET_CELL_OBJECT_3 ((x), (o))) SCM_API SCM scm_i_allocate_weak_vector (scm_t_bits type, SCM size, SCM fill);