summaryrefslogtreecommitdiff
path: root/class.c
diff options
context:
space:
mode:
authorYusuke Endoh <mame@ruby-lang.org>2021-11-02 19:23:36 +0900
committerYusuke Endoh <mame@ruby-lang.org>2021-11-09 16:11:10 +0900
commit428227472fc6563046d8138aab17f07bef6af753 (patch)
tree57e713aa4a0c6192198ea7183f486e561b30a820 /class.c
parent3ff0a0b40c2e1fbdad2286f1dafe837f822d0e0d (diff)
class.c: calculate the length of Class.descendants in advance
GC must not be triggered during callback of rb_class_foreach_subclass. To prevent GC, we can not use rb_ary_push. Instead, this changeset calls rb_class_foreach_subclass twice: first counts the subclasses, then allocates a buffer (which may cause GC and reduce subclasses, but not increase), and finally stores the subclasses to the buffer. [Bug #18282] [Feature #14394]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/5070
Diffstat (limited to 'class.c')
-rw-r--r--class.c35
1 files changed, 29 insertions, 6 deletions
diff --git a/class.c b/class.c
index 6bf17aaa47..a11285fc97 100644
--- a/class.c
+++ b/class.c
@@ -124,6 +124,8 @@ rb_class_foreach_subclass(VALUE klass, void (*f)(VALUE, VALUE), VALUE arg)
while (cur) {
VALUE curklass = cur->klass;
cur = cur->next;
+ // do not trigger GC during f, otherwise the cur will become
+ // a dangling pointer if the subclass is collected
f(curklass, arg);
}
}
@@ -1334,13 +1336,24 @@ rb_mod_ancestors(VALUE mod)
return ary;
}
+struct subclass_traverse_data
+{
+ VALUE *buffer;
+ long count;
+};
+
static void
-class_descendants_recursive(VALUE klass, VALUE ary)
+class_descendants_recursive(VALUE klass, VALUE v)
{
+ struct subclass_traverse_data *data = (struct subclass_traverse_data *) v;
+
if (BUILTIN_TYPE(klass) == T_CLASS && !FL_TEST(klass, FL_SINGLETON)) {
- rb_ary_push(ary, klass);
+ if (data->buffer) {
+ data->buffer[data->count] = klass;
+ }
+ data->count++;
}
- rb_class_foreach_subclass(klass, class_descendants_recursive, ary);
+ rb_class_foreach_subclass(klass, class_descendants_recursive, v);
}
/*
@@ -1364,9 +1377,19 @@ class_descendants_recursive(VALUE klass, VALUE ary)
VALUE
rb_class_descendants(VALUE klass)
{
- VALUE ary = rb_ary_new();
- rb_class_foreach_subclass(klass, class_descendants_recursive, ary);
- return ary;
+ struct subclass_traverse_data data = { NULL, 0 };
+
+ // estimate the count of subclasses
+ rb_class_foreach_subclass(klass, class_descendants_recursive, (VALUE) &data);
+
+ // this allocation may cause GC which may reduce the subclasses
+ data.buffer = ALLOCA_N(VALUE, data.count);
+ data.count = 0;
+
+ // enumerate subclasses
+ rb_class_foreach_subclass(klass, class_descendants_recursive, (VALUE) &data);
+
+ return rb_ary_new_from_values(data.count, data.buffer);
}
static void