Skip to content

Commit

Permalink
Reimplement RData on top of RTypedData
Browse files Browse the repository at this point in the history
The goal is to eliminate `RTypedData.typed_flag` which waste 8B,
allowing `TypedData` to fit in a potential `32B` slot.

This however makes `RData` 48B long, but given it's a deprecated
construct it seems acceptable to degrade it to improve its replacement.
  • Loading branch information
byroot committed Jun 10, 2024
1 parent 448efa9 commit 556f462
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 98 deletions.
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 @@ -2973,35 +2973,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 @@ -3011,7 +2995,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 @@ -3024,13 +3008,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 @@ -3043,7 +3028,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 @@ -3068,6 +3053,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 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), &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 @@ -3190,31 +3225,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 @@ -3372,7 +3405,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 @@ -4339,7 +4372,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 && (!DATA_PTR(obj) || !RDATA(obj)->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

0 comments on commit 556f462

Please sign in to comment.