diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2018-03-21 00:58:24 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2018-03-21 00:58:24 +0300 |
commit | b04b346006b34ec371fb8f002ba56522ee033131 (patch) | |
tree | 92b1fa5df920181a1c1752e7a219554ec6f3fdc2 /finalize.c | |
parent | 2e8c66c62b648aadb4929b9f23ede47eea0a43f5 (diff) | |
download | bdwgc-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.c | 25 |
1 files changed, 20 insertions, 5 deletions
@@ -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 |