Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement RData on top of RTypedData #558

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 80 additions & 49 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ typedef struct RVALUE {
struct RArray array;
struct RRegexp regexp;
struct RHash hash;
struct RData data;
struct RDataHeader data;
struct RTypedData typeddata;
struct RStruct rstruct;
struct RBignum bignum;
Expand Down Expand Up @@ -2974,35 +2974,19 @@ rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags,
static inline void
rb_data_object_check(VALUE klass)
{
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
rb_undef_alloc_func(klass);
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
}
}

VALUE
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
{
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
if (klass) rb_data_object_check(klass);
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData));
}

VALUE
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
{
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
DATA_PTR(obj) = xcalloc(1, size);
return obj;
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
rb_undef_alloc_func(klass);
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
}
}

static VALUE
typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size)
typed_data_alloc(VALUE klass, const rb_data_type_t *type, void *datap, size_t size)
{
RBIMPL_NONNULL_ARG(type);
if (klass) rb_data_object_check(klass);
bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark;
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size);
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, (VALUE)datap, 0, wb_protected, size);
}

VALUE
Expand All @@ -3012,7 +2996,7 @@ rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData");
}

return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData));
return typed_data_alloc(klass, type, datap, sizeof(struct RTypedData));
}

VALUE
Expand All @@ -3025,13 +3009,14 @@ rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type

size_t embed_size = offsetof(struct RTypedData, data) + size;
if (rb_gc_size_allocatable_p(embed_size)) {
VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size);
VALUE obj = typed_data_alloc(klass, type, NULL, embed_size);
memset((char *)obj + offsetof(struct RTypedData, data), 0, size);
FL_SET_RAW(obj, TYPED_DATA_FL_EMBEDDED);
return obj;
}
}

VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData));
VALUE obj = typed_data_alloc(klass, type, NULL, sizeof(struct RTypedData));
DATA_PTR(obj) = xcalloc(1, size);
return obj;
}
Expand All @@ -3044,7 +3029,7 @@ rb_objspace_data_type_memsize(VALUE obj)
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
const void *ptr = RTYPEDDATA_GET_DATA(obj);

if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
#ifdef HAVE_MALLOC_USABLE_SIZE
size += malloc_usable_size((void *)ptr);
#endif
Expand All @@ -3069,6 +3054,56 @@ rb_objspace_data_type_name(VALUE obj)
}
}

static void
mark_deprecated_rdata_object(void *ptr)
{
struct RData *rdata = (struct RData *)ptr;
if (rdata->dmark) {
rdata->dmark(rdata);
}
}

static size_t
memsize_deprecated_rdata_object(const void *ptr)
{
return sizeof(struct RData);
}

#define DEPRECATED_DATA_FREE RBIMPL_DATA_FUNC(-3)

const rb_data_type_t ruby_deprecated_rdata_type = {
.wrap_struct_name = "RDATA(deprecated)",
.function = {
.dmark = mark_deprecated_rdata_object,
.dfree = DEPRECATED_DATA_FREE,
.dsize = memsize_deprecated_rdata_object,
},
};

VALUE
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
{
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
if (klass) rb_data_object_check(klass);

VALUE obj = rb_data_typed_object_zalloc(klass, sizeof(struct RData), &ruby_deprecated_rdata_type);

struct RData *rdata = (struct RData *)obj;
rdata->dmark = dmark;
rdata->dfree = dfree;
rdata->data = datap;
return obj;
}

VALUE
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
{
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
struct RData *rdata = (struct RData *)obj;
rdata->data = xcalloc(1, size);
return obj;
}

static int
ptr_in_page_body_p(const void *ptr, const void *memb)
{
Expand Down Expand Up @@ -3191,31 +3226,29 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
}

static bool
rb_data_free(rb_objspace_t *objspace, VALUE obj)
rb_typeddata_free(rb_objspace_t *objspace, VALUE obj)
{
void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
void *data = RTYPEDDATA_GET_DATA(obj);
if (data) {
int free_immediately = false;
void (*dfree)(void *);

if (RTYPEDDATA_P(obj)) {
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
dfree = RANY(obj)->as.typeddata.type->function.dfree;
}
else {
dfree = RANY(obj)->as.data.dfree;
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;

RUBY_DATA_FUNC dfree = RANY(obj)->as.typeddata.type->function.dfree;
if (UNLIKELY(dfree == DEPRECATED_DATA_FREE)) {
dfree = RDATA(obj)->dfree;
}

if (dfree) {
if (dfree == RUBY_DEFAULT_FREE) {
if (!RTYPEDDATA_EMBEDDED_P(obj)) {
if (!rbimpl_rtypeddata_embedded_p(obj)) {
xfree(data);
RB_DEBUG_COUNTER_INC(obj_data_xfree);
}
}
else if (free_immediately) {
(*dfree)(data);
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
xfree(data);
}

Expand Down Expand Up @@ -3373,7 +3406,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
}
break;
case T_DATA:
if (!rb_data_free(objspace, obj)) return false;
if (!rb_typeddata_free(objspace, obj)) return false;
break;
case T_MATCH:
{
Expand Down Expand Up @@ -4330,7 +4363,7 @@ rb_objspace_call_finalizer_i(VALUE obj, void *data)

switch (BUILTIN_TYPE(obj)) {
case T_DATA:
if (!rb_free_at_exit && (!DATA_PTR(obj) || !RANY(obj)->as.data.dfree)) break;
if (!rb_free_at_exit && (!RTYPEDDATA_GET_DATA(obj) || !RANY(obj)->as.typeddata.type->function.dfree)) break;
if (rb_obj_is_thread(obj)) break;
if (rb_obj_is_mutex(obj)) break;
if (rb_obj_is_fiber(obj)) break;
Expand Down Expand Up @@ -6954,20 +6987,18 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)

case T_DATA:
{
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
void *const ptr = RTYPEDDATA_GET_DATA(obj);

if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
if (gc_declarative_marking_p(any->as.typeddata.type)) {
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;

for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset));
}
}
else {
RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
any->as.typeddata.type->function.dmark :
any->as.data.dmark;
RUBY_DATA_FUNC mark_func = any->as.typeddata.type->function.dmark;
if (mark_func) (*mark_func)(ptr);
}
}
Expand Down Expand Up @@ -10170,9 +10201,9 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
case T_DATA:
/* Call the compaction callback, if it exists */
{
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
void *const ptr = RTYPEDDATA_GET_DATA(obj);
if (ptr) {
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
if (gc_declarative_marking_p(any->as.typeddata.type)) {
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;

for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
Expand All @@ -10181,7 +10212,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
*ref = rb_gc_location(*ref);
}
}
else if (RTYPEDDATA_P(obj)) {
else {
RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact;
if (compact_func) (*compact_func)(ptr);
}
Expand Down Expand Up @@ -13306,7 +13337,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
rb_raw_iseq_info(BUFF_ARGS, iseq);
}
else if (rb_ractor_p(obj)) {
rb_ractor_t *r = (void *)DATA_PTR(obj);
rb_ractor_t *r = (void *)RTYPEDDATA_GET_DATA(obj);
if (r) {
APPEND_F("r:%d", r->pub.id);
}
Expand Down
30 changes: 17 additions & 13 deletions include/ruby/internal/core/rdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "ruby/internal/attr/warning.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/core/rbasic.h"
#include "ruby/internal/core/rtypeddata.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/fl_type.h"
#include "ruby/internal/value.h"
Expand Down Expand Up @@ -77,6 +78,7 @@
*/
#define RUBY_DEFAULT_FREE RBIMPL_DATA_FUNC(-1)


/**
* This is a value you can set to ::RData::dfree. Setting this means the data
* is managed by someone else, like, statically allocated. Of course you are
Expand All @@ -93,16 +95,6 @@
*/
#define RUBY_UNTYPED_DATA_FUNC(f) f RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()

/*
#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func))
*/

/**
* This is the type of callbacks registered to ::RData. The argument is the
* `data` field.
*/
typedef void (*RUBY_DATA_FUNC)(void*);

/**
* @deprecated
*
Expand All @@ -117,11 +109,26 @@ typedef void (*RUBY_DATA_FUNC)(void*);
* too many warnings in the core. Maybe we want to retry later... Just add
* deprecated document for now.
*/
struct RDataHeader {
/** Basic part, including flags and class. */
struct RBasic basic;

const rb_data_type_t *const type;

/** Pointer to the actual C level struct that you want to wrap. */
void *data;
};

struct RData {

/** Basic part, including flags and class. */
struct RBasic basic;

const rb_data_type_t *const type;

/** Pointer to the actual C level struct that you want to wrap. */
void *data;

/**
* This function is called when the object is experiencing GC marks. If it
* contains references to other Ruby objects, you need to mark them also.
Expand All @@ -141,9 +148,6 @@ struct RData {
* impossible at that moment (that is why GC runs).
*/
RUBY_DATA_FUNC dfree;

/** Pointer to the actual C level struct that you want to wrap. */
void *data;
};

RBIMPL_SYMBOL_EXPORT_BEGIN()
Expand Down
Loading
Loading