summaryrefslogtreecommitdiff
path: root/finalize.c
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2018-03-21 00:58:24 +0300
committerIvan Maidanski <ivmai@mail.ru>2018-03-21 00:58:24 +0300
commitb04b346006b34ec371fb8f002ba56522ee033131 (patch)
tree92b1fa5df920181a1c1752e7a219554ec6f3fdc2 /finalize.c
parent2e8c66c62b648aadb4929b9f23ede47eea0a43f5 (diff)
downloadbdwgc-b04b346006b34ec371fb8f002ba56522ee033131.tar.gz
Fix unbounded heap growth in case of intensive disappearing links usage
Issue #182 (bdwgc). * finalize.c (GC_ON_GROW_LOG_SIZE_MIN): New macro. * finalize.c (GC_grow_table): Rename size_ptr to log_size_ptr in comment; add entries_ptr argument; add comment; call if log_old_size is GC_ON_GROW_LOG_SIZE_MIN or bigger then call GC_try_to_collect_inner and, then, return from the function if the number of entries is less than 75% of the table capacity. * finalize.c (GC_register_disappearing_link_inner): Pass dl_hashtbl->entries pointer to GC_grow_table. * finalize.c (GC_register_finalizer_inner): Pass GC_fo_entries pointer to GC_grow_table.
Diffstat (limited to 'finalize.c')
-rw-r--r--finalize.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/finalize.c b/finalize.c
index 4b513fcf..cbe00b82 100644
--- a/finalize.c
+++ b/finalize.c
@@ -99,12 +99,18 @@ GC_API void GC_CALL GC_push_finalizer_structures(void)
GC_PUSH_ALL_SYM(GC_fnlz_roots);
}
-/* Double the size of a hash table. *size_ptr is the log of its current */
-/* size. May be a no-op. */
+/* Threshold of log_size to initiate full collection before growing */
+/* a hash table. */
+#ifndef GC_ON_GROW_LOG_SIZE_MIN
+# define GC_ON_GROW_LOG_SIZE_MIN 12
+#endif
+
+/* Double the size of a hash table. *log_size_ptr is the log of its */
+/* current size. May be a no-op. */
/* *table is a pointer to an array of hash headers. If we succeed, we */
/* update both *table and *log_size_ptr. Lock is held. */
STATIC void GC_grow_table(struct hash_chain_entry ***table,
- signed_word *log_size_ptr)
+ signed_word *log_size_ptr, word *entries_ptr)
{
word i;
struct hash_chain_entry *p;
@@ -116,6 +122,15 @@ STATIC void GC_grow_table(struct hash_chain_entry ***table,
struct hash_chain_entry **new_table;
GC_ASSERT(I_HOLD_LOCK());
+ /* Avoid growing the table in case of at least 25% of entries can */
+ /* be deleted by enforcing a collection. Ignored for small tables. */
+ if (log_old_size >= GC_ON_GROW_LOG_SIZE_MIN) {
+ (void)GC_try_to_collect_inner(GC_never_stop_func);
+ /* GC_finalize might decrease entries value. */
+ if (*entries_ptr < ((word)1 << log_old_size) - (*entries_ptr >> 2))
+ return;
+ }
+
new_table = (struct hash_chain_entry **)
GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
(size_t)new_size * sizeof(struct hash_chain_entry *),
@@ -168,7 +183,7 @@ STATIC int GC_register_disappearing_link_inner(
if (dl_hashtbl -> log_size == -1
|| dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) {
GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head,
- &dl_hashtbl -> log_size);
+ &dl_hashtbl -> log_size, &dl_hashtbl -> entries);
# ifdef LINT2
if (dl_hashtbl->log_size < 0) ABORT("log_size is negative");
# endif
@@ -665,7 +680,7 @@ STATIC void GC_register_finalizer_inner(void * obj,
if (log_fo_table_size == -1
|| GC_fo_entries > ((word)1 << log_fo_table_size)) {
GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head,
- &log_fo_table_size);
+ &log_fo_table_size, &GC_fo_entries);
# ifdef LINT2
if (log_fo_table_size < 0) ABORT("log_size is negative");
# endif