Released stashes could cause SEGV
(2010/11/22 追記: 5.12ではこの問題は解決され、SEGVはおきなくなっている。)
Released CvGVs could cause SEGVの注で「シンボルテーブル <-> GV間にも循環参照があるため同じ問題を抱えるのだが,こちらは真のウィークリファレンスを導入して解決した」と書いたが、よく調べてみると解決しているとは言いがたい状態だった。
まず、スタッシュ(Symbol Table Hash)とGV(type glob)の関係だが、スタッシュはGVを「所有」している*1。一方、GVはフィールドに所属しているスタッシュを持ち、CレベルではGvSTASH(gv)として参照できるのだが、このGVからスタッシュへの関係はウィークリファレンスとなっている。したがって、不要なGVやスタッシュは適切に解放される。しかし、このデータ構造は、GVが存在しつつ所属元のスタッシュが解放されるという事が起こり得る*2。
以下のスクリプトはそのような状況を作り出す。
#!perl use strict; use Devel::Peek; my $gv = *Foo::bar; delete $::{'Foo::'}; # 所属元のスタッシュを削除する Dump $gv; __END__
結果:
SV = PVGV(0x1004de68) at 0x1004f668 REFCNT = 1 FLAGS = (PADMY,FAKE,MULTI,IMPORTALL) NAME = "bar" NAMELEN = 3 GvSTASH = 0x0 GP = 0x10067b50 SV = 0x0 REFCNT = 2 IO = 0x0 FORM = 0x0 AV = 0x0 HV = 0x0 CV = 0x0 CVGEN = 0x0 LINE = 6 FILE = "stash.pl" FLAGS = 0xf2 EGV = 0x1004f710 "bar"
このように GvSTASH = 0x0 となっており、ウィークリファレンスメカニズムが適切に働いていることが分かる。
しかし、このGVはデータとしては壊れており、何か操作を加えるとすぐSEGVする。
#!perl use strict; use Devel::Peek; my $gv = *Foo::bar; delete $::{'Foo::'}; print $gv; # SEGV Dump($gv); __END__
これはGvSTASHを参照している箇所でNULLかどうかをチェックすればよいので、問題の解決はそれほど困難でなないと思われる。
よって[あとで報告する]。