Skip to content

Commit

Permalink
cc->cme should not be marked.
Browse files Browse the repository at this point in the history
cc is callcache.

cc->klass (klass) should not be marked because if the klass is
free'ed, the cc->klass will be cleared by `vm_cc_invalidate()`.

cc->cme (cme) should not be marked because if cc is invalidated
when cme is free'ed.
- klass marks cme if klass uses cme.
- caller classe's ccs->cme marks cc->cme.
- if cc is invalidated (klass doesn't refer the cc),
  cc is invalidated by `vm_cc_invalidate()` and cc->cme is
  not be accessed.
- On the multi-Ractors, cme will be collected with global GC
  so that it is safe if GC is not interleaving while accessing
  cc and cme.

fix [Bug #19436]

```ruby
10_000.times{|i|
  # p i if (i%1_000) == 0

  str = "x" * 1_000_000
  def str.foo = nil
  eval "def call#{i}(s) = s.foo"
  send "call#{i}", str
}
```

Without this patch:

```
real    1m5.639s
user    0m6.637s
sys     0m58.292s
```

and with this patch:

```
real    0m2.045s
user    0m1.627s
sys     0m0.164s
```
  • Loading branch information
Ruby committed Jul 28, 2023
1 parent 44b19b0 commit ebb28d6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 31 deletions.
33 changes: 23 additions & 10 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7015,11 +7015,22 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
case imemo_callinfo:
return;
case imemo_callcache:
{
const struct rb_callcache *cc = (const struct rb_callcache *)obj;
// should not mark klass here
gc_mark(objspace, (VALUE)vm_cc_cme(cc));
}
/* cc is callcache.
*
* cc->klass (klass) should not be marked because if the klass is
* free'ed, the cc->klass will be cleared by `vm_cc_invalidate()`.
*
* cc->cme (cme) should not be marked because if cc is invalidated
* when cme is free'ed.
* - klass marks cme if klass uses cme.
* - caller classe's ccs->cme marks cc->cme.
* - if cc is invalidated (klass doesn't refer the cc),
* cc is invalidated by `vm_cc_invalidate()` and cc->cme is
* not be accessed.
* - On the multi-Ractors, cme will be collected with global GC
* so that it is safe if GC is not interleaving while accessing
* cc and cme.
*/
return;
case imemo_constcache:
{
Expand Down Expand Up @@ -10122,12 +10133,14 @@ gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj)
if (!is_live_object(objspace, cc->klass)) {
*((VALUE *)(&cc->klass)) = (VALUE)0;
}
}

if (cc->cme_) {
TYPED_UPDATE_IF_MOVED(objspace, struct rb_callable_method_entry_struct *, cc->cme_);
if (!is_live_object(objspace, (VALUE)cc->cme_)) {
*((struct rb_callable_method_entry_struct **)(&cc->cme_)) = (struct rb_callable_method_entry_struct *)0;
// cc->cme_ is available if cc->klass is given

if (cc->cme_) {
TYPED_UPDATE_IF_MOVED(objspace, struct rb_callable_method_entry_struct *, cc->cme_);
if (!is_live_object(objspace, (VALUE)cc->cme_)) {
*((struct rb_callable_method_entry_struct **)(&cc->cme_)) = (struct rb_callable_method_entry_struct *)0;
}
}
}
}
Expand Down
49 changes: 28 additions & 21 deletions iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,29 @@ rb_iseq_mark_and_move_each_value(const rb_iseq_t *iseq, VALUE *original_iseq)
}
}

static bool
cc_is_active(const struct rb_callcache *cc, bool reference_updating)
{
if (cc) {
if (reference_updating) {
cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
}

if (vm_cc_markable(cc)) {
if (cc->klass) { // cc is not invalidated
const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
if (reference_updating) {
cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
}
if (!METHOD_ENTRY_INVALIDATED(cme)) {
return true;
}
}
}
}
return false;
}

void
rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
{
Expand Down Expand Up @@ -310,27 +333,11 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)

if (cds[i].ci) rb_gc_mark_and_move_ptr(&cds[i].ci);

const struct rb_callcache *cc = cds[i].cc;
if (cc) {
if (reference_updating) {
cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
}

if (vm_cc_markable(cc)) {
VM_ASSERT((cc->flags & VM_CALLCACHE_ON_STACK) == 0);

const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
if (reference_updating) {
cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
}

if (cc->klass && !METHOD_ENTRY_INVALIDATED(cme)) {
rb_gc_mark_and_move_ptr(&cds[i].cc);
}
else {
cds[i].cc = rb_vm_empty_cc();
}
}
if (cc_is_active(cds[i].cc, reference_updating)) {
rb_gc_mark_and_move_ptr(&cds[i].cc);
}
else {
cds[i].cc = rb_vm_empty_cc();
}
}
}
Expand Down

0 comments on commit ebb28d6

Please sign in to comment.