summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gc.c51
-rw-r--r--include/ruby/internal/core/rtypeddata.h9
-rw-r--r--include/ruby/internal/gc.h6
3 files changed, 60 insertions, 6 deletions
diff --git a/gc.c b/gc.c
index c269d7fce0..06a4bacfb8 100644
--- a/gc.c
+++ b/gc.c
@@ -7241,6 +7241,39 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
}
}
+static bool
+gc_declarative_marking_p(const rb_data_type_t *type)
+{
+ return (type->flags & RUBY_TYPED_DECL_MARKING) != 0;
+}
+
+#define EDGE (VALUE *)((char *)data_struct + offset)
+
+static inline void
+gc_mark_from_offset(rb_objspace_t *objspace, VALUE obj)
+{
+ // we are overloading the dmark callback to contain a list of offsets
+ size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
+ void *data_struct = RANY(obj)->as.typeddata.data;
+
+ for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
+ rb_gc_mark_movable(*EDGE);
+ }
+}
+
+static inline void
+gc_ref_update_from_offset(rb_objspace_t *objspace, VALUE obj)
+{
+ // we are overloading the dmark callback to contain a list of offsets
+ size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
+ void *data_struct = RANY(obj)->as.typeddata.data;
+
+ for (size_t offset = *offset_list; *offset_list != RUBY_REF_END; offset = *offset_list++) {
+ if (SPECIAL_CONST_P(*EDGE)) continue;
+ *EDGE = rb_gc_location(*EDGE);
+ }
+}
+
static void
gc_mark_children(rb_objspace_t *objspace, VALUE obj)
{
@@ -7350,10 +7383,15 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
{
void *const ptr = DATA_PTR(obj);
if (ptr) {
- RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
- any->as.typeddata.type->function.dmark :
- any->as.data.dmark;
- if (mark_func) (*mark_func)(ptr);
+ if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
+ gc_mark_from_offset(objspace, obj);
+ }
+ else {
+ RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
+ any->as.typeddata.type->function.dmark :
+ any->as.data.dmark;
+ if (mark_func) (*mark_func)(ptr);
+ }
}
}
break;
@@ -10606,7 +10644,10 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
{
void *const ptr = DATA_PTR(obj);
if (ptr) {
- if (RTYPEDDATA_P(obj)) {
+ if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
+ gc_ref_update_from_offset(objspace, obj);
+ }
+ else if (RTYPEDDATA_P(obj)) {
RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact;
if (compact_func) (*compact_func)(ptr);
}
diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h
index bbf208867d..9d91a3e3f1 100644
--- a/include/ruby/internal/core/rtypeddata.h
+++ b/include/ruby/internal/core/rtypeddata.h
@@ -176,7 +176,14 @@ rbimpl_typeddata_flags {
* This flag is mysterious. It seems nobody is currently using it. The
* intention of this flag is also unclear. We need further investigations.
*/
- RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1 /* THIS FLAG DEPENDS ON Ruby version */
+ RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1, /* THIS FLAG DEPENDS ON Ruby version */
+
+ /**
+ * This flag determines whether marking and compaction should be carried out
+ * using the dmark/dcompact callback functions or whether we should mark
+ * declaratively using a list of references defined inside the data struct we're wrapping
+ */
+ RUBY_TYPED_DECL_MARKING = RUBY_FL_USER2
};
/**
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
index fb14b34211..bc81883e0e 100644
--- a/include/ruby/internal/gc.h
+++ b/include/ruby/internal/gc.h
@@ -44,6 +44,12 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()
+#define REF_EDGE(s, p) (offsetof(struct s, p))
+#define REFS_LIST_PTR(l) ((RUBY_DATA_FUNC)l)
+#define RUBY_REF_END SIZE_MAX
+#define RUBY_REFERENCES_START(t) static size_t t[] = {
+#define RUBY_REFERENCES_END RUBY_REF_END, };
+
/* gc.c */
RBIMPL_ATTR_COLD()