summaryrefslogtreecommitdiff
path: root/libguile/weak-set.c
diff options
context:
space:
mode:
Diffstat (limited to 'libguile/weak-set.c')
-rw-r--r--libguile/weak-set.c38
1 files changed, 37 insertions, 1 deletions
diff --git a/libguile/weak-set.c b/libguile/weak-set.c
index a1ae4ea6b..476e88627 100644
--- a/libguile/weak-set.c
+++ b/libguile/weak-set.c
@@ -308,6 +308,42 @@ compute_size_index (scm_t_weak_set *set)
return i;
}
+static int
+is_acceptable_size_index (scm_t_weak_set *set, int size_index)
+{
+ int computed = compute_size_index (set);
+
+ if (size_index == computed)
+ /* We were going to grow or shrink, and allocating the new vector
+ didn't change the target size. */
+ return 1;
+
+ if (size_index == computed + 1)
+ {
+ /* We were going to enlarge the set, but allocating the new
+ vector finalized some objects, making an enlargement
+ unnecessary. It might still be a good idea to use the larger
+ set, though. (This branch also gets hit if, while allocating
+ the vector, some other thread was actively removing items from
+ the set. That is less likely, though.) */
+ unsigned long new_lower = hashset_size[size_index] / 5;
+
+ return set->size > new_lower;
+ }
+
+ if (size_index == computed - 1)
+ {
+ /* We were going to shrink the set, but when we dropped the lock
+ to allocate the new vector, some other thread added elements to
+ the set. */
+ return 0;
+ }
+
+ /* The computed size differs from our newly allocated size by more
+ than one size index -- recalculate. */
+ return 0;
+}
+
static void
resize_set (scm_t_weak_set *set)
{
@@ -328,7 +364,7 @@ resize_set (scm_t_weak_set *set)
"weak set");
scm_i_pthread_mutex_unlock (&set->lock);
}
- while (new_size_index != compute_size_index (set));
+ while (!is_acceptable_size_index (set, new_size_index));
old_entries = set->entries;
old_size = set->size;