diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2020-12-31 00:56:19 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2020-12-31 00:56:19 -0800 |
commit | 21f4b2adb8e238ed996ec676056b77e9560b08d9 (patch) | |
tree | 05a6f828cca8092dc221d93f473114f1f8bdf304 | |
parent | a24a7da08b3caba01a47e84974fb6efe928594bc (diff) | |
download | txr-21f4b2adb8e238ed996ec676056b77e9560b08d9.tar.gz txr-21f4b2adb8e238ed996ec676056b77e9560b08d9.tar.bz2 txr-21f4b2adb8e238ed996ec676056b77e9560b08d9.zip |
gc: bug: finalization logic causing gc assert
This problem was introduced on Feb 18, 2019 in commit
0ed46d885dba49ac2c2628d742cbbd7b6afea9fb.
Explicit finalization of objects outside of GC can
trigger an assertion in a later GC pass.
* gc.c (call_finalizers_impl): We simply must not have
duplicate entries in fresh_obj; this causes problems for
sweep_one. Under Valgrind debugging, sweep_one protects the
memory of processed entries, so a duplicate visit triggers bad
accesses. A more serious issue is that this function can be
called outside of gc, explicitly, since it is available as an
API function. So that is to say, non-garbage objects can have
their finalizers called ouside of GC. If this function is
called explicitly, outside of GC, it can end up doing
something stupid, like adding a generation 1 object into the
freshobj array, triggering an assert. We address this by not
doing any of that processing if GC is not taking place.
-rw-r--r-- | gc.c | 24 |
1 files changed, 17 insertions, 7 deletions
@@ -783,16 +783,26 @@ static val call_finalizers_impl(val ctx, while (found) { struct fin_reg *next = found->next; + int dup, i, freshobj_idx_start = freshobj_idx; val obj = found->obj; funcall1(found->fun, obj); #if CONFIG_GEN_GC - /* Note: here an object may be added to freshobj more than once, since - * multiple finalizers can be registered. - */ - if (freshobj_idx < FRESHOBJ_VEC_SIZE && obj->t.gen == 0) - freshobj[freshobj_idx++] = obj; - else - full_gc = 1; + if (inprogress) { + for (dup = 0, i = freshobj_idx_start; i < freshobj_idx; i++) { + if (freshobj[i] == obj) { + dup = 1; + break; + } + } + + if (!dup) { + if (freshobj_idx < FRESHOBJ_VEC_SIZE && obj->t.gen == 0) { + freshobj[freshobj_idx++] = obj; + } else { + full_gc = 1; + } + } + } #endif free(found); found = next; |