Skip to content

Commit 45907b1

Browse files
committed
add SET_SHAREABLE macros
* `RB_OBJ_SET_SHAREABLE(obj)` makes obj shareable. All of reachable objects from `obj` should be shareable. * `RB_OBJ_SET_FROZEN_SHAREABLE(obj)` same as above but freeze `obj` before making it shareable. Also `rb_gc_verify_shareable(obj)` is introduced to check the `obj` does not violate shareable rule (an shareable object only refers shareable objects) strictly. The rule has some exceptions (some shareable objects can refer to unshareable objects, such as a Ractor object (which is a shareable object) can refer to the Ractor local objects. To handle such case, `check_shareable` flag is also introduced. `STRICT_VERIFY_SHAREABLE` macro is also introduced to verify the strict shareable rule at `SET_SHAREABLE`.
1 parent 271be0a commit 45907b1

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

gc/default/default.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ typedef struct rb_objspace {
491491
unsigned int during_minor_gc : 1;
492492
unsigned int during_incremental_marking : 1;
493493
unsigned int measure_gc : 1;
494+
unsigned int check_shareable : 1;
494495
} flags;
495496

496497
rb_event_flag_t hook_events;
@@ -1455,6 +1456,19 @@ RVALUE_WHITE_P(rb_objspace_t *objspace, VALUE obj)
14551456
return !RVALUE_MARKED(objspace, obj);
14561457
}
14571458

1459+
bool
1460+
rb_gc_impl_checking_shareable(void *objspace_ptr)
1461+
{
1462+
rb_objspace_t *objspace = objspace_ptr;
1463+
return objspace->flags.check_shareable;
1464+
}
1465+
1466+
bool
1467+
rb_gc_checking_shareable(void)
1468+
{
1469+
return rb_gc_impl_checking_shareable(rb_gc_get_objspace());
1470+
}
1471+
14581472
bool
14591473
rb_gc_impl_gc_enabled_p(void *objspace_ptr)
14601474
{
@@ -4962,6 +4976,52 @@ check_children_i(const VALUE child, void *ptr)
49624976
}
49634977
}
49644978

4979+
static void
4980+
check_shareable_i(const VALUE child, void *ptr)
4981+
{
4982+
struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr;
4983+
4984+
if (!RB_OBJ_SHAREABLE_P(child)) {
4985+
fprintf(stderr, "(a) ");
4986+
rp(data->parent);
4987+
fprintf(stderr, "(b) ");
4988+
rp(child);
4989+
fprintf(stderr, "check_shareable_i: shareable (a) -> unshareable (b)\n");
4990+
4991+
data->err_count++;
4992+
rb_bug("!! violate shareable constraint !!");
4993+
}
4994+
}
4995+
4996+
static void
4997+
gc_verify_shareable(rb_objspace_t *objspace, VALUE obj, void *data)
4998+
{
4999+
// while objspace->flags.check_shareable is true,
5000+
// other Ractors should not run the GC, until the flag is not local.
5001+
// TODO: remove VM locking if the flag is Ractor local
5002+
RB_VM_LOCKING() {
5003+
objspace->flags.check_shareable = true;
5004+
rb_objspace_reachable_objects_from(obj, check_shareable_i, (void *)data);
5005+
objspace->flags.check_shareable = false;
5006+
}
5007+
}
5008+
5009+
// TODO: only one level (non-recursive)
5010+
void
5011+
rb_gc_verify_shareable(VALUE obj)
5012+
{
5013+
rb_objspace_t *objspace = rb_gc_get_objspace();
5014+
struct verify_internal_consistency_struct data = {
5015+
.parent = obj,
5016+
.err_count = 0,
5017+
};
5018+
gc_verify_shareable(objspace, obj, &data);
5019+
5020+
if (data.err_count > 0) {
5021+
rb_bug("rb_gc_verify_shareable");
5022+
}
5023+
}
5024+
49655025
static int
49665026
verify_internal_consistency_i(void *page_start, void *page_end, size_t stride,
49675027
struct verify_internal_consistency_struct *data)
@@ -6645,6 +6705,7 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_
66456705
gc_enter_count(event);
66466706
if (RB_UNLIKELY(during_gc != 0)) rb_bug("during_gc != 0");
66476707
if (RGENGC_CHECK_MODE >= 3) gc_verify_internal_consistency(objspace);
6708+
GC_ASSERT(!objspace->flags.check_shareable);
66486709

66496710
during_gc = TRUE;
66506711
RUBY_DEBUG_LOG("%s (%s)",gc_enter_event_cstr(event), gc_current_status(objspace));
@@ -6658,6 +6719,7 @@ static inline void
66586719
gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev)
66596720
{
66606721
GC_ASSERT(during_gc != 0);
6722+
GC_ASSERT(!objspace->flags.check_shareable);
66616723

66626724
rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_EXIT);
66636725

include/ruby/ractor.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,18 @@ rb_ractor_shareable_p(VALUE obj)
261261
}
262262
}
263263

264+
// TODO: optimize on interpreter core
265+
#ifndef RB_OBJ_SET_SHAREABLE
266+
VALUE rb_obj_set_shareable(VALUE obj); // ractor.c
267+
#define RB_OBJ_SET_SHAREABLE(obj) rb_obj_set_shareable(obj)
268+
#endif
269+
270+
static inline VALUE
271+
RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj)
272+
{
273+
RB_OBJ_FREEZE(obj);
274+
RB_OBJ_SET_SHAREABLE(obj);
275+
return obj;
276+
}
277+
264278
#endif /* RUBY_RACTOR_H */

internal/gc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,7 @@ ruby_sized_realloc_n(void *ptr, size_t new_count, size_t element_size, size_t ol
351351
#define ruby_sized_xrealloc ruby_sized_xrealloc_inlined
352352
#define ruby_sized_xrealloc2 ruby_sized_xrealloc2_inlined
353353
#define ruby_sized_xfree ruby_sized_xfree_inlined
354+
355+
void rb_gc_verify_shareable(VALUE);
356+
354357
#endif /* INTERNAL_GC_H */

ractor.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,44 @@ rb_ractor_hooks(rb_ractor_t *cr)
11131113
return &cr->pub.hooks;
11141114
}
11151115

1116+
static void
1117+
rb_obj_set_shareable_no_assert(VALUE obj)
1118+
{
1119+
FL_SET_RAW(obj, FL_SHAREABLE);
1120+
1121+
if (rb_obj_exivar_p(obj)) {
1122+
VALUE fields = rb_obj_fields_no_ractor_check(obj);
1123+
if (imemo_type_p(fields, imemo_fields)) {
1124+
// no recursive mark
1125+
FL_SET_RAW(fields, FL_SHAREABLE);
1126+
}
1127+
}
1128+
}
1129+
1130+
#ifndef STRICT_VERIFY_SHAREABLE
1131+
#define STRICT_VERIFY_SHAREABLE 0
1132+
#endif
1133+
1134+
bool
1135+
rb_ractor_verify_shareable(VALUE obj)
1136+
{
1137+
#if STRICT_VERIFY_SHAREABLE
1138+
rb_gc_verify_shareable(obj);
1139+
#endif
1140+
return true;
1141+
}
1142+
1143+
VALUE
1144+
rb_obj_set_shareable(VALUE obj)
1145+
{
1146+
RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
1147+
1148+
rb_obj_set_shareable_no_assert(obj);
1149+
RUBY_ASSERT(rb_ractor_verify_shareable(obj));
1150+
1151+
return obj;
1152+
}
1153+
11161154
/// traverse function
11171155

11181156
// 2: stop search
@@ -1239,6 +1277,8 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12391277

12401278
case T_ARRAY:
12411279
{
1280+
rb_ary_cancel_sharing(obj);
1281+
12421282
for (int i = 0; i < RARRAY_LENINT(obj); i++) {
12431283
VALUE e = rb_ary_entry(obj, i);
12441284
if (obj_traverse_i(e, data)) return 1;
@@ -1422,7 +1462,7 @@ make_shareable_check_shareable(VALUE obj)
14221462
static enum obj_traverse_iterator_result
14231463
mark_shareable(VALUE obj)
14241464
{
1425-
FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
1465+
rb_obj_set_shareable_no_assert(obj);
14261466
return traverse_cont;
14271467
}
14281468

0 commit comments

Comments
 (0)