diff options
-rw-r--r-- | gc.c | 51 | ||||
-rw-r--r-- | include/ruby/internal/core/rtypeddata.h | 9 | ||||
-rw-r--r-- | include/ruby/internal/gc.h | 6 |
3 files changed, 60 insertions, 6 deletions
@@ -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() |