Skip to content

Commit baf5831

Browse files
committed
Use rb_gc_mark_weak for cc->klass.
One of the biggest remaining contention point is `RClass.cc_table`. The logical solution would be to turn it into a managed object, so we can use an RCU strategy, given it's read heavy. However, that's not currently possible because the table can't be freed before the owning class, given the class free function MUST go over all the CC entries to invalidate them. However if the `CC->klass` reference is weak marked, then the GC will take care of setting the reference to `Qundef`.
1 parent cbe5241 commit baf5831

8 files changed

Lines changed: 48 additions & 40 deletions

File tree

debug_counter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ RB_DEBUG_COUNTER(cc_temp) // dummy CC (stack-allocated)
4949
RB_DEBUG_COUNTER(cc_found_in_ccs) // count for CC lookup success in CCS
5050
RB_DEBUG_COUNTER(cc_not_found_in_ccs) // count for CC lookup success in CCS
5151

52-
RB_DEBUG_COUNTER(cc_ent_invalidate) // count for invalidating cc (cc->klass = 0)
52+
RB_DEBUG_COUNTER(cc_ent_invalidate) // count for invalidating cc (cc->klass = Qundef)
5353
RB_DEBUG_COUNTER(cc_cme_invalidate) // count for invalidating CME
5454

5555
RB_DEBUG_COUNTER(cc_invalidate_leaf) // count for invalidating klass if klass has no-subclasses

gc.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,7 @@ classext_free(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
12141214

12151215
rb_id_table_free(RCLASSEXT_M_TBL(ext));
12161216
rb_cc_tbl_free(RCLASSEXT_CC_TBL(ext), args->klass);
1217+
12171218
if (!RCLASSEXT_SHARED_CONST_TBL(ext) && (tbl = RCLASSEXT_CONST_TBL(ext)) != NULL) {
12181219
rb_free_const_table(tbl);
12191220
}
@@ -4929,11 +4930,11 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
49294930
case imemo_callcache:
49304931
{
49314932
const struct rb_callcache *cc = (const struct rb_callcache *)obj;
4932-
VALUE class_path = cc->klass ? rb_class_path_cached(cc->klass) : Qnil;
4933+
VALUE class_path = vm_cc_valid(cc) ? rb_class_path_cached(cc->klass) : Qnil;
49334934
const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
49344935

49354936
APPEND_F("(klass:%s cme:%s%s (%p) call:%p",
4936-
NIL_P(class_path) ? (cc->klass ? "??" : "<NULL>") : RSTRING_PTR(class_path),
4937+
NIL_P(class_path) ? (vm_cc_valid(cc) ? "??" : "<NULL>") : RSTRING_PTR(class_path),
49374938
cme ? rb_id2name(cme->called_id) : "<NULL>",
49384939
cme ? (METHOD_ENTRY_INVALIDATED(cme) ? " [inv]" : "") : "",
49394940
(void *)cme,

imemo.c

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -353,25 +353,20 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
353353
*/
354354
struct rb_callcache *cc = (struct rb_callcache *)obj;
355355
if (reference_updating) {
356-
if (!cc->klass) {
357-
// already invalidated
356+
if (moved_or_living_object_strictly_p(cc->klass) &&
357+
moved_or_living_object_strictly_p((VALUE)cc->cme_)) {
358+
*((VALUE *)&cc->klass) = rb_gc_location(cc->klass);
359+
*((struct rb_callable_method_entry_struct **)&cc->cme_) =
360+
(struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cc->cme_);
358361
}
359-
else {
360-
if (moved_or_living_object_strictly_p(cc->klass) &&
361-
moved_or_living_object_strictly_p((VALUE)cc->cme_)) {
362-
*((VALUE *)&cc->klass) = rb_gc_location(cc->klass);
363-
*((struct rb_callable_method_entry_struct **)&cc->cme_) =
364-
(struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cc->cme_);
365-
}
366-
else {
367-
vm_cc_invalidate(cc);
368-
}
362+
else if (vm_cc_valid(cc)) {
363+
vm_cc_invalidate(cc);
369364
}
370365
}
371366
else {
372-
if (cc->klass && (vm_cc_super_p(cc) || vm_cc_refinement_p(cc))) {
367+
rb_gc_mark_weak((VALUE *)&cc->klass);
368+
if ((vm_cc_super_p(cc) || vm_cc_refinement_p(cc))) {
373369
rb_gc_mark_movable((VALUE)cc->cme_);
374-
rb_gc_mark_movable((VALUE)cc->klass);
375370
}
376371
}
377372

iseq.c

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,13 @@ cc_is_active(const struct rb_callcache *cc, bool reference_updating)
325325
cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
326326
}
327327

328-
if (vm_cc_markable(cc)) {
329-
if (cc->klass) { // cc is not invalidated
330-
const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
331-
if (reference_updating) {
332-
cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
333-
}
334-
if (!METHOD_ENTRY_INVALIDATED(cme)) {
335-
return true;
336-
}
328+
if (vm_cc_markable(cc) && vm_cc_valid(cc)) {
329+
const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
330+
if (reference_updating) {
331+
cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
332+
}
333+
if (!METHOD_ENTRY_INVALIDATED(cme)) {
334+
return true;
337335
}
338336
}
339337
}

vm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ rb_serial_t ruby_vm_global_cvar_state = 1;
607607

608608
static const struct rb_callcache vm_empty_cc = {
609609
.flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
610-
.klass = Qfalse,
610+
.klass = Qundef,
611611
.cme_ = NULL,
612612
.call_ = vm_call_general,
613613
.aux_ = {
@@ -617,7 +617,7 @@ static const struct rb_callcache vm_empty_cc = {
617617

618618
static const struct rb_callcache vm_empty_cc_for_super = {
619619
.flags = T_IMEMO | (imemo_callcache << FL_USHIFT) | VM_CALLCACHE_UNMARKABLE,
620-
.klass = Qfalse,
620+
.klass = Qundef,
621621
.cme_ = NULL,
622622
.call_ = vm_call_super_method,
623623
.aux_ = {

vm_callinfo.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,7 @@ struct rb_callcache {
279279
const VALUE flags;
280280

281281
/* inline cache: key */
282-
const VALUE klass; // should not mark it because klass can not be free'd
283-
// because of this marking. When klass is collected,
284-
// cc will be cleared (cc->klass = 0) at vm_ccs_free().
282+
const VALUE klass; // Weak reference. When klass is collected, `cc->klass = Qundef`.
285283

286284
/* inline cache: values */
287285
const struct rb_callable_method_entry_struct * const cme_;
@@ -324,12 +322,20 @@ vm_cc_attr_index_initialize(const struct rb_callcache *cc, shape_id_t shape_id)
324322
vm_cc_attr_index_set(cc, (attr_index_t)-1, shape_id);
325323
}
326324

325+
static inline VALUE
326+
cc_check_class(VALUE klass)
327+
{
328+
VM_ASSERT(klass == Qundef || RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_ICLASS));
329+
return klass;
330+
}
331+
327332
static inline const struct rb_callcache *
328333
vm_cc_new(VALUE klass,
329334
const struct rb_callable_method_entry_struct *cme,
330335
vm_call_handler call,
331336
enum vm_cc_type type)
332337
{
338+
cc_check_class(klass);
333339
struct rb_callcache *cc = IMEMO_NEW(struct rb_callcache, imemo_callcache, klass);
334340
*((struct rb_callable_method_entry_struct **)&cc->cme_) = (struct rb_callable_method_entry_struct *)cme;
335341
*((vm_call_handler *)&cc->call_) = call;
@@ -374,7 +380,7 @@ vm_cc_refinement_p(const struct rb_callcache *cc)
374380
(imemo_callcache << FL_USHIFT) | \
375381
VM_CALLCACHE_UNMARKABLE | \
376382
VM_CALLCACHE_ON_STACK, \
377-
.klass = clazz, \
383+
.klass = cc_check_class(clazz), \
378384
.cme_ = cme, \
379385
.call_ = call, \
380386
.aux_ = aux, \
@@ -384,8 +390,7 @@ static inline bool
384390
vm_cc_class_check(const struct rb_callcache *cc, VALUE klass)
385391
{
386392
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
387-
VM_ASSERT(cc->klass == 0 ||
388-
RB_TYPE_P(cc->klass, T_CLASS) || RB_TYPE_P(cc->klass, T_ICLASS));
393+
VM_ASSERT(cc_check_class(cc->klass));
389394
return cc->klass == klass;
390395
}
391396

@@ -396,6 +401,15 @@ vm_cc_markable(const struct rb_callcache *cc)
396401
return FL_TEST_RAW((VALUE)cc, VM_CALLCACHE_UNMARKABLE) == 0;
397402
}
398403

404+
static inline bool
405+
vm_cc_valid(const struct rb_callcache *cc)
406+
{
407+
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
408+
VM_ASSERT(cc_check_class(cc->klass));
409+
410+
return !UNDEF_P(cc->klass);
411+
}
412+
399413
static inline const struct rb_callable_method_entry_struct *
400414
vm_cc_cme(const struct rb_callcache *cc)
401415
{
@@ -447,7 +461,7 @@ vm_cc_cmethod_missing_reason(const struct rb_callcache *cc)
447461
static inline bool
448462
vm_cc_invalidated_p(const struct rb_callcache *cc)
449463
{
450-
if (cc->klass && !METHOD_ENTRY_INVALIDATED(vm_cc_cme(cc))) {
464+
if (vm_cc_valid(cc) && !METHOD_ENTRY_INVALIDATED(vm_cc_cme(cc))) {
451465
return false;
452466
}
453467
else {
@@ -543,9 +557,9 @@ vm_cc_invalidate(const struct rb_callcache *cc)
543557
{
544558
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
545559
VM_ASSERT(cc != vm_cc_empty());
546-
VM_ASSERT(cc->klass != 0); // should be enable
560+
VM_ASSERT(cc->klass != Qundef); // should be enable
547561

548-
*(VALUE *)&cc->klass = 0;
562+
*(VALUE *)&cc->klass = Qundef;
549563
RB_DEBUG_COUNTER_INC(cc_ent_invalidate);
550564
}
551565

vm_eval.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ static inline VALUE vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, i
5757
VALUE
5858
rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *cme, int kw_splat)
5959
{
60-
const struct rb_callcache cc = VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme);
60+
const struct rb_callcache cc = VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme);
6161
return vm_call0_cc(ec, recv, id, argc, argv, &cc, kw_splat);
6262
}
6363

@@ -104,7 +104,7 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
104104
static VALUE
105105
vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme)
106106
{
107-
calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme);
107+
calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme);
108108
return vm_call0_body(ec, calling, argv);
109109
}
110110

vm_method.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ invalidate_cc_refinement(st_data_t key, st_data_t data)
409409

410410
VM_ASSERT(vm_cc_refinement_p(cc));
411411

412-
if (cc->klass) {
412+
if (vm_cc_valid(cc)) {
413413
vm_cc_invalidate(cc);
414414
}
415415
}

0 commit comments

Comments
 (0)