PerlIOのメモリアロケーション
PerlIOのデータ構造の前に。
CレベルのPerlIO*については、PerlIOシステムがメモリ管理をしてくれる。そのメモリ管理を行うのがPerlIO_allocate()で、これが呼び出されると利用していないPerlIO*ポインタを探して返す。利用されていないPerlIO*ポインタがなければ、malloc(3)でまとめてメモリを割り当てる。
この「利用しているか否か」というのが曖昧な定義となっており、現状では少なくとも「Perlからアクセスできるか否か」という意味ではない。したがって、この曖昧な定義を利用して奇妙な動作を引き起こすことが出来る。
以下、そのコードを示す。
#!perl -w use strict; use 5.010_000; use PerlIO::Util; open my $fh1, '<', \''; 1 while $fh1->pop_layer(); { open my $fh2, '<', '/dev/null'; say 'fh1 ', $fh1->inspect(); say 'fh2 ', $fh2->inspect(); } say 'fh1 ', $fh1->inspect();
実行結果:
fh1 PerlIO 0x1003e228 0x10066c40:perlio(3) CANREAD FASTGETS 0x10044838:unix(3) CANREAD OPEN fh2 PerlIO 0x1003e228 0x10066c40:perlio(3) CANREAD FASTGETS 0x10044838:unix(3) CANREAD OPEN fh1 PerlIO 0x1003e228 (Invalid filehandle)
$fh1と$fh2が持つPerlIO* fpが同じ値(0x1003e228)になっている。
"1 while $fh1->pop_layer();"によって$fh1の実体であるPerlIO* fpが空(PerlIO_valid(fp)が偽)になるのだが、その結果$fh1の持つPerlIO* fpはPerlIOアロケータに「利用されていない」と判断される。その結果として、PerlIOアロケータが$fh2のためのPerlIO*を要求されたときに、$fh1の持つPerlIO* fpを返す。そのため元々無効なファイルハンドルであった$fh1までも、一時的に$fh2と同じく有効になるが、$fh2のスコープが終わり自動的にクローズされると、$fh1もまた無効なファイルハンドルに戻る。
そして、その後も新しいファイルハンドルを開くと、$fh1はその新しいファイルと同一になる。