Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / weak-table.c
index 2810e0b..e911069 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011 Free Software Foundation, Inc.
+/* Copyright (C) 2011, 2012, 2013 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
@@ -127,17 +127,17 @@ register_disappearing_links (scm_t_weak_entry *entry,
                              SCM k, SCM v,
                              scm_t_weak_table_kind kind)
 {
-  if (SCM_UNPACK (k) && SCM_NIMP (k)
+  if (SCM_UNPACK (k) && SCM_HEAP_OBJECT_P (k)
       && (kind == SCM_WEAK_TABLE_KIND_KEY
           || kind == SCM_WEAK_TABLE_KIND_BOTH))
-    SCM_I_REGISTER_DISAPPEARING_LINK ((GC_PTR) &entry->key,
-                                      (GC_PTR) SCM2PTR (k));
+    SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entry->key,
+                                      SCM2PTR (k));
 
-  if (SCM_UNPACK (v) && SCM_NIMP (v)
+  if (SCM_UNPACK (v) && SCM_HEAP_OBJECT_P (v)
       && (kind == SCM_WEAK_TABLE_KIND_VALUE
           || kind == SCM_WEAK_TABLE_KIND_BOTH))
-    SCM_I_REGISTER_DISAPPEARING_LINK ((GC_PTR) &entry->value,
-                                      (GC_PTR) SCM2PTR (v));
+    SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entry->value,
+                                      SCM2PTR (v));
 }
 
 static void
@@ -145,10 +145,32 @@ unregister_disappearing_links (scm_t_weak_entry *entry,
                                scm_t_weak_table_kind kind)
 {
   if (kind == SCM_WEAK_TABLE_KIND_KEY || kind == SCM_WEAK_TABLE_KIND_BOTH)
-    GC_unregister_disappearing_link ((GC_PTR) &entry->key);
+    GC_unregister_disappearing_link ((void **) &entry->key);
 
   if (kind == SCM_WEAK_TABLE_KIND_VALUE || kind == SCM_WEAK_TABLE_KIND_BOTH)
-    GC_unregister_disappearing_link ((GC_PTR) &entry->value);
+    GC_unregister_disappearing_link ((void **) &entry->value);
+}
+
+#ifndef HAVE_GC_MOVE_DISAPPEARING_LINK
+static void
+GC_move_disappearing_link (void **from, void **to)
+{
+  GC_unregister_disappearing_link (from);
+  SCM_I_REGISTER_DISAPPEARING_LINK (to, *to);
+}
+#endif
+
+static void
+move_disappearing_links (scm_t_weak_entry *from, scm_t_weak_entry *to,
+                         SCM key, SCM value, scm_t_weak_table_kind kind)
+{
+  if ((kind == SCM_WEAK_TABLE_KIND_KEY || kind == SCM_WEAK_TABLE_KIND_BOTH)
+      && SCM_HEAP_OBJECT_P (key))
+    GC_move_disappearing_link ((void **) &from->key, (void **) &to->key);
+
+  if ((kind == SCM_WEAK_TABLE_KIND_VALUE || kind == SCM_WEAK_TABLE_KIND_BOTH)
+      && SCM_HEAP_OBJECT_P (value))
+    GC_move_disappearing_link ((void **) &from->value, (void **) &to->value);
 }
 
 static void
@@ -164,10 +186,9 @@ move_weak_entry (scm_t_weak_entry *from, scm_t_weak_entry *to,
       to->key = copy.key;
       to->value = copy.value;
 
-      unregister_disappearing_links (from, kind);
-      register_disappearing_links (to,
-                                   SCM_PACK (copy.key), SCM_PACK (copy.value),
-                                   kind);
+      move_disappearing_links (from, to,
+                               SCM_PACK (copy.key), SCM_PACK (copy.value),
+                               kind);
     }
   else
     {
@@ -378,28 +399,22 @@ static unsigned long hashtable_size[] = {
 
 #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
 
-static void
-resize_table (scm_t_weak_table *table)
+static int
+compute_size_index (scm_t_weak_table *table)
 {
-  scm_t_weak_entry *old_entries, *new_entries;
-  int i;
-  unsigned long old_size, new_size, old_k;
+  int i = table->size_index;
 
-  old_entries = table->entries;
-  old_size = table->size;
-  
   if (table->n_items < table->lower)
     {
       /* rehashing is not triggered when i <= min_size */
-      i = table->size_index;
       do
        --i;
       while (i > table->min_size_index
-            && table->n_items < hashtable_size[i] / 4);
+            && table->n_items < hashtable_size[i] / 5);
     }
-  else
+  else if (table->n_items > table->upper)
     {
-      i = table->size_index + 1;
+      ++i;
       if (i >= HASHTABLE_SIZE_N)
         /* The biggest size currently is 230096423, which for a 32-bit
            machine will occupy 2.3GB of memory at a load of 80%.  There
@@ -408,12 +423,68 @@ resize_table (scm_t_weak_table *table)
         abort ();
     }
 
-  new_size = hashtable_size[i];
-  new_entries = allocate_entries (new_size, table->kind);
+  return i;
+}
 
-  table->size_index = i;
+static int
+is_acceptable_size_index (scm_t_weak_table *table, int size_index)
+{
+  int computed = compute_size_index (table);
+
+  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 table, but allocating the new
+         vector finalized some objects, making an enlargement
+         unnecessary.  It might still be a good idea to use the larger
+         table, though.  (This branch also gets hit if, while allocating
+         the vector, some other thread was actively removing items from
+         the table.  That is less likely, though.)  */
+      unsigned long new_lower = hashtable_size[size_index] / 5;
+
+      return table->size > new_lower;
+    }
+
+  if (size_index == computed - 1)
+    {
+      /* We were going to shrink the table, but when we dropped the lock
+         to allocate the new vector, some other thread added elements to
+         the table.  */
+      return 0;
+    }
+
+  /* The computed size differs from our newly allocated size by more
+     than one size index -- recalculate.  */
+  return 0;
+}
+
+static void
+resize_table (scm_t_weak_table *table)
+{
+  scm_t_weak_entry *old_entries, *new_entries;
+  int new_size_index;
+  unsigned long old_size, new_size, old_k;
+
+  do 
+    {
+      new_size_index = compute_size_index (table);
+      if (new_size_index == table->size_index)
+        return;
+      new_size = hashtable_size[new_size_index];
+      new_entries = allocate_entries (new_size, table->kind);
+    }
+  while (!is_acceptable_size_index (table, new_size_index));
+
+  old_entries = table->entries;
+  old_size = table->size;
+  
+  table->size_index = new_size_index;
   table->size = new_size;
-  if (i <= table->min_size_index)
+  if (new_size_index <= table->min_size_index)
     table->lower = 0;
   else
     table->lower = new_size / 5;
@@ -719,12 +790,12 @@ make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
 void
 scm_i_weak_table_print (SCM exp, SCM port, scm_print_state *pstate)
 {
-  scm_puts ("#<", port);
-  scm_puts ("weak-table ", port);
+  scm_puts_unlocked ("#<", port);
+  scm_puts_unlocked ("weak-table ", port);
   scm_uintprint (SCM_WEAK_TABLE (exp)->n_items, 10, port);
-  scm_putc ('/', port);
+  scm_putc_unlocked ('/', port);
   scm_uintprint (SCM_WEAK_TABLE (exp)->size, 10, port);
-  scm_puts (">", port);
+  scm_puts_unlocked (">", port);
 }
 
 static void
@@ -734,6 +805,14 @@ do_vacuum_weak_table (SCM table)
 
   t = SCM_WEAK_TABLE (table);
 
+  /* Unlike weak sets, the weak table interface allows custom predicates
+     to call out to arbitrary Scheme.  There are two ways that this code
+     can be re-entrant, then: calling weak hash procedures while in a
+     custom predicate, or via finalizers run explicitly by (gc) or in an
+     async (for non-threaded Guile).  We add a restriction that
+     prohibits the first case, by convention.  But since we can't
+     prohibit the second case, here we trylock instead of lock.  Not so
+     nice.  */
   if (scm_i_pthread_mutex_trylock (&t->lock) == 0)
     {
       vacuum_weak_table (t);
@@ -743,56 +822,6 @@ do_vacuum_weak_table (SCM table)
   return;
 }
 
-/* The before-gc C hook only runs if GC_table_start_callback is available,
-   so if not, fall back on a finalizer-based implementation.  */
-static int
-weak_gc_callback (void **weak)
-{
-  void *val = weak[0];
-  void (*callback) (SCM) = weak[1];
-  
-  if (!val)
-    return 0;
-  
-  callback (SCM_PACK_POINTER (val));
-
-  return 1;
-}
-
-#ifdef HAVE_GC_TABLE_START_CALLBACK
-static void*
-weak_gc_hook (void *hook_data, void *fn_data, void *data)
-{
-  if (!weak_gc_callback (fn_data))
-    scm_c_hook_remove (&scm_before_gc_c_hook, weak_gc_hook, fn_data);
-
-  return NULL;
-}
-#else
-static void
-weak_gc_finalizer (void *ptr, void *data)
-{
-  if (weak_gc_callback (ptr))
-    GC_REGISTER_FINALIZER_NO_ORDER (ptr, weak_gc_finalizer, data, NULL, NULL);
-}
-#endif
-
-static void
-scm_c_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
-{
-  void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
-
-  weak[0] = SCM_UNPACK_POINTER (obj);
-  weak[1] = (void*)callback;
-  GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
-
-#ifdef HAVE_GC_TABLE_START_CALLBACK
-  scm_c_hook_add (&scm_after_gc_c_hook, weak_gc_hook, weak, 0);
-#else
-  GC_REGISTER_FINALIZER_NO_ORDER (weak, weak_gc_finalizer, NULL, NULL, NULL);
-#endif
-}
-
 SCM
 scm_c_make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
 {
@@ -800,7 +829,7 @@ scm_c_make_weak_table (unsigned long k, scm_t_weak_table_kind kind)
 
   ret = make_weak_table (k, kind);
 
-  scm_c_register_weak_gc_callback (ret, do_vacuum_weak_table);
+  scm_i_register_weak_gc_callback (ret, do_vacuum_weak_table);
 
   return ret;
 }
@@ -891,24 +920,22 @@ scm_weak_table_refq (SCM table, SCM key, SCM dflt)
                                dflt);
 }
 
-SCM
+void
 scm_weak_table_putq_x (SCM table, SCM key, SCM value)
 {
   scm_c_weak_table_put_x (table, scm_ihashq (key, -1),
                           assq_predicate, SCM_UNPACK_POINTER (key),
                           key, value);
-  return SCM_UNSPECIFIED;
 }
 
-SCM
+void
 scm_weak_table_remq_x (SCM table, SCM key)
 {
   scm_c_weak_table_remove_x (table, scm_ihashq (key, -1),
                              assq_predicate, SCM_UNPACK_POINTER (key));
-  return SCM_UNSPECIFIED;
 }
 
-SCM
+void
 scm_weak_table_clear_x (SCM table)
 #define FUNC_NAME "weak-table-clear!"
 {
@@ -924,8 +951,6 @@ scm_weak_table_clear_x (SCM table)
   t->n_items = 0;
 
   scm_i_pthread_mutex_unlock (&t->lock);
-
-  return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
 
@@ -993,7 +1018,7 @@ for_each_trampoline (void *closure, SCM k, SCM v, SCM seed)
   return seed;
 }
 
-SCM
+void
 scm_weak_table_for_each (SCM proc, SCM table)
 #define FUNC_NAME "weak-table-for-each"
 {
@@ -1001,8 +1026,6 @@ scm_weak_table_for_each (SCM proc, SCM table)
   SCM_VALIDATE_PROC (1, proc);
 
   scm_c_weak_table_fold (for_each_trampoline, SCM_UNPACK_POINTER (proc), SCM_BOOL_F, table);
-
-  return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME