`cc->cme` should not be marked.

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
```
This commit is contained in:
Ruby 2023-07-26 13:39:31 +09:00 коммит произвёл Koichi Sasada
Родитель 44b19b01e2
Коммит c330037c1a
2 изменённых файлов: 51 добавлений и 31 удалений

33
gc.c
Просмотреть файл

@ -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:
{
@ -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;
}
}
}
}

49
iseq.c
Просмотреть файл

@ -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)
{
@ -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();
}
}
}