Skip to content

Commit c5158ef

Browse files
committed
Reimplement RData on top of RTypedData
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.
1 parent bf061ce commit c5158ef

File tree

4 files changed

+142
-98
lines changed

4 files changed

+142
-98
lines changed

gc.c

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ typedef struct RVALUE {
661661
struct RArray array;
662662
struct RRegexp regexp;
663663
struct RHash hash;
664-
struct RData data;
664+
struct RDataHeader data;
665665
struct RTypedData typeddata;
666666
struct RStruct rstruct;
667667
struct RBignum bignum;
@@ -2974,35 +2974,19 @@ rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags,
29742974
static inline void
29752975
rb_data_object_check(VALUE klass)
29762976
{
2977-
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
2978-
rb_undef_alloc_func(klass);
2979-
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
2980-
}
2981-
}
2982-
2983-
VALUE
2984-
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
2985-
{
2986-
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
2987-
if (klass) rb_data_object_check(klass);
2988-
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData));
2989-
}
2990-
2991-
VALUE
2992-
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
2993-
{
2994-
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
2995-
DATA_PTR(obj) = xcalloc(1, size);
2996-
return obj;
2977+
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
2978+
rb_undef_alloc_func(klass);
2979+
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
2980+
}
29972981
}
29982982

29992983
static VALUE
3000-
typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size)
2984+
typed_data_alloc(VALUE klass, const rb_data_type_t *type, void *datap, size_t size)
30012985
{
30022986
RBIMPL_NONNULL_ARG(type);
30032987
if (klass) rb_data_object_check(klass);
30042988
bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark;
3005-
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size);
2989+
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, (VALUE)datap, 0, wb_protected, size);
30062990
}
30072991

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

3015-
return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData));
2999+
return typed_data_alloc(klass, type, datap, sizeof(struct RTypedData));
30163000
}
30173001

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

30263010
size_t embed_size = offsetof(struct RTypedData, data) + size;
30273011
if (rb_gc_size_allocatable_p(embed_size)) {
3028-
VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size);
3012+
VALUE obj = typed_data_alloc(klass, type, NULL, embed_size);
30293013
memset((char *)obj + offsetof(struct RTypedData, data), 0, size);
3014+
FL_SET_RAW(obj, TYPED_DATA_FL_EMBEDDED);
30303015
return obj;
30313016
}
30323017
}
30333018

3034-
VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData));
3019+
VALUE obj = typed_data_alloc(klass, type, NULL, sizeof(struct RTypedData));
30353020
DATA_PTR(obj) = xcalloc(1, size);
30363021
return obj;
30373022
}
@@ -3044,7 +3029,7 @@ rb_objspace_data_type_memsize(VALUE obj)
30443029
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
30453030
const void *ptr = RTYPEDDATA_GET_DATA(obj);
30463031

3047-
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
3032+
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
30483033
#ifdef HAVE_MALLOC_USABLE_SIZE
30493034
size += malloc_usable_size((void *)ptr);
30503035
#endif
@@ -3069,6 +3054,56 @@ rb_objspace_data_type_name(VALUE obj)
30693054
}
30703055
}
30713056

3057+
static void
3058+
mark_deprecated_rdata_object(void *ptr)
3059+
{
3060+
struct RData *rdata = (struct RData *)ptr;
3061+
if (rdata->dmark) {
3062+
rdata->dmark(rdata);
3063+
}
3064+
}
3065+
3066+
static size_t
3067+
memsize_deprecated_rdata_object(const void *ptr)
3068+
{
3069+
return sizeof(struct RData);
3070+
}
3071+
3072+
#define DEPRECATED_DATA_FREE RBIMPL_DATA_FUNC(-3)
3073+
3074+
const rb_data_type_t deprecated_rdata_type = {
3075+
.wrap_struct_name = "RDATA(deprecated)",
3076+
.function = {
3077+
.dmark = mark_deprecated_rdata_object,
3078+
.dfree = DEPRECATED_DATA_FREE,
3079+
.dsize = memsize_deprecated_rdata_object,
3080+
},
3081+
};
3082+
3083+
VALUE
3084+
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
3085+
{
3086+
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
3087+
if (klass) rb_data_object_check(klass);
3088+
3089+
VALUE obj = rb_data_typed_object_zalloc(klass, sizeof(struct RData), &deprecated_rdata_type);
3090+
3091+
struct RData *rdata = (struct RData *)obj;
3092+
rdata->dmark = dmark;
3093+
rdata->dfree = dfree;
3094+
rdata->data = datap;
3095+
return obj;
3096+
}
3097+
3098+
VALUE
3099+
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
3100+
{
3101+
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
3102+
struct RData *rdata = (struct RData *)obj;
3103+
rdata->data = xcalloc(1, size);
3104+
return obj;
3105+
}
3106+
30723107
static int
30733108
ptr_in_page_body_p(const void *ptr, const void *memb)
30743109
{
@@ -3191,31 +3226,29 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
31913226
}
31923227

31933228
static bool
3194-
rb_data_free(rb_objspace_t *objspace, VALUE obj)
3229+
rb_typeddata_free(rb_objspace_t *objspace, VALUE obj)
31953230
{
3196-
void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
3231+
void *data = RTYPEDDATA_GET_DATA(obj);
31973232
if (data) {
31983233
int free_immediately = false;
3199-
void (*dfree)(void *);
32003234

3201-
if (RTYPEDDATA_P(obj)) {
3202-
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
3203-
dfree = RANY(obj)->as.typeddata.type->function.dfree;
3204-
}
3205-
else {
3206-
dfree = RANY(obj)->as.data.dfree;
3235+
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
3236+
3237+
RUBY_DATA_FUNC dfree = RANY(obj)->as.typeddata.type->function.dfree;
3238+
if (UNLIKELY(dfree == DEPRECATED_DATA_FREE)) {
3239+
dfree = RDATA(obj)->dfree;
32073240
}
32083241

32093242
if (dfree) {
32103243
if (dfree == RUBY_DEFAULT_FREE) {
3211-
if (!RTYPEDDATA_EMBEDDED_P(obj)) {
3244+
if (!rbimpl_rtypeddata_embedded_p(obj)) {
32123245
xfree(data);
32133246
RB_DEBUG_COUNTER_INC(obj_data_xfree);
32143247
}
32153248
}
32163249
else if (free_immediately) {
32173250
(*dfree)(data);
3218-
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
3251+
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
32193252
xfree(data);
32203253
}
32213254

@@ -3373,7 +3406,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
33733406
}
33743407
break;
33753408
case T_DATA:
3376-
if (!rb_data_free(objspace, obj)) return false;
3409+
if (!rb_typeddata_free(objspace, obj)) return false;
33773410
break;
33783411
case T_MATCH:
33793412
{
@@ -4330,7 +4363,7 @@ rb_objspace_call_finalizer_i(VALUE obj, void *data)
43304363

43314364
switch (BUILTIN_TYPE(obj)) {
43324365
case T_DATA:
4333-
if (!rb_free_at_exit && (!DATA_PTR(obj) || !RANY(obj)->as.data.dfree)) break;
4366+
if (!rb_free_at_exit && (!DATA_PTR(obj) || !RDATA(obj)->dfree)) break;
43344367
if (rb_obj_is_thread(obj)) break;
43354368
if (rb_obj_is_mutex(obj)) break;
43364369
if (rb_obj_is_fiber(obj)) break;
@@ -6954,20 +6987,18 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
69546987

69556988
case T_DATA:
69566989
{
6957-
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
6990+
void *const ptr = RTYPEDDATA_GET_DATA(obj);
69586991

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

69636996
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
69646997
rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset));
69656998
}
69666999
}
69677000
else {
6968-
RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
6969-
any->as.typeddata.type->function.dmark :
6970-
any->as.data.dmark;
7001+
RUBY_DATA_FUNC mark_func = any->as.typeddata.type->function.dmark;
69717002
if (mark_func) (*mark_func)(ptr);
69727003
}
69737004
}
@@ -10170,9 +10201,9 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
1017010201
case T_DATA:
1017110202
/* Call the compaction callback, if it exists */
1017210203
{
10173-
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
10204+
void *const ptr = RTYPEDDATA_GET_DATA(obj);
1017410205
if (ptr) {
10175-
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
10206+
if (gc_declarative_marking_p(any->as.typeddata.type)) {
1017610207
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
1017710208

1017810209
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@@ -10181,7 +10212,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
1018110212
*ref = rb_gc_location(*ref);
1018210213
}
1018310214
}
10184-
else if (RTYPEDDATA_P(obj)) {
10215+
else {
1018510216
RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact;
1018610217
if (compact_func) (*compact_func)(ptr);
1018710218
}
@@ -13306,7 +13337,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
1330613337
rb_raw_iseq_info(BUFF_ARGS, iseq);
1330713338
}
1330813339
else if (rb_ractor_p(obj)) {
13309-
rb_ractor_t *r = (void *)DATA_PTR(obj);
13340+
rb_ractor_t *r = (void *)RTYPEDDATA_GET_DATA(obj);
1331013341
if (r) {
1331113342
APPEND_F("r:%d", r->pub.id);
1331213343
}

include/ruby/internal/core/rdata.h

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "ruby/internal/attr/warning.h"
3131
#include "ruby/internal/cast.h"
3232
#include "ruby/internal/core/rbasic.h"
33+
#include "ruby/internal/core/rtypeddata.h"
3334
#include "ruby/internal/dllexport.h"
3435
#include "ruby/internal/fl_type.h"
3536
#include "ruby/internal/value.h"
@@ -77,6 +78,7 @@
7778
*/
7879
#define RUBY_DEFAULT_FREE RBIMPL_DATA_FUNC(-1)
7980

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

96-
/*
97-
#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func))
98-
*/
99-
100-
/**
101-
* This is the type of callbacks registered to ::RData. The argument is the
102-
* `data` field.
103-
*/
104-
typedef void (*RUBY_DATA_FUNC)(void*);
105-
10698
/**
10799
* @deprecated
108100
*
@@ -117,11 +109,26 @@ typedef void (*RUBY_DATA_FUNC)(void*);
117109
* too many warnings in the core. Maybe we want to retry later... Just add
118110
* deprecated document for now.
119111
*/
112+
struct RDataHeader {
113+
/** Basic part, including flags and class. */
114+
struct RBasic basic;
115+
116+
const rb_data_type_t *const type;
117+
118+
/** Pointer to the actual C level struct that you want to wrap. */
119+
void *data;
120+
};
121+
120122
struct RData {
121123

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

127+
const rb_data_type_t *const type;
128+
129+
/** Pointer to the actual C level struct that you want to wrap. */
130+
void *data;
131+
125132
/**
126133
* This function is called when the object is experiencing GC marks. If it
127134
* contains references to other Ruby objects, you need to mark them also.
@@ -141,9 +148,6 @@ struct RData {
141148
* impossible at that moment (that is why GC runs).
142149
*/
143150
RUBY_DATA_FUNC dfree;
144-
145-
/** Pointer to the actual C level struct that you want to wrap. */
146-
void *data;
147151
};
148152

149153
RBIMPL_SYMBOL_EXPORT_BEGIN()

0 commit comments

Comments
 (0)