トップ 差分 一覧 Farm ソース 検索 ヘルプ RSS ログイン

BBS-サポート掲示板/204

ページの作成や編集にはユーザ登録が必要です。

 mod_perlでセッションファイルが更新されない - まかまか (2004年03月28日 21時22分24秒)

3.5.2をApache::Registry(perl,v5.8.0 built for i386-linux-thread-multi,Apache/1.3.27, mod_perl 1.28)で利用しておりますが、ログインした後、ページを移動してもセッションファイルが更新されません。

BBS-サポート掲示板/45にもあるように、CGI::Sessionオブジェクトのデストラクタが呼び出されていないようなのでとりあえず応急処置としてwiki.cgiの最後に

if(defined $ENV{MOD_PERL} and defined(my $ses = $wiki->get_CGI->{session_cache})){
	$ses->flush;
}

を追加して、セッションファイルを強制的に更新して使っています。この現象は通常は起きないのでしょうか…?


すいません、自己解決しました。CGI::Session::FileとCGI2のインスタンスが循環参照していてるため、デストラクタが呼び出されないのが原因でした。また、このせいでApache::Registryではメモリリークとなります。とりあえずwikiを終了する前に

$wiki->get_CGI->{session_cache}->{_SESSION_OBJ} = undef;
$wiki->get_CGI->{session_cache}                 = undef;

すればよいのですが、これ以外にもWikiとストレージ、WikiとParserで循環参照していてメモリリークを起こしています。

提案なのですが、Wiki.pmに後処理用メソッドを実装し

例:

sub exit{
	my $self = shift;
	$self->do_hook('finalize'); # プラグイン用
	undef( $self->get_CGI->{session_cache}->{_SESSION_OBJ} );
	undef( $self->get_CGI->{session_cache} );
	undef( $self->{storage}->{wiki} );
	……などなど
	exit;
}

wikiを終了させる際には(可能なら他のプラグインで呼び出されるexitも$wiki->exitに置き換えるなどして)、後処理ができるようにするのはどうでしょうか。

  • 仰る通りexit関数の代用品を用意することも考えたのですが、そもそも循環参照しないようにできないかと考えています。でも今のインターフェースでは難しいですよね…。考えてみます。 - たけぞう (2004年04月02日 11時30分13秒)
  • あと、Wiki.pm←→DefaultStorage.pmは確かに循環参照しているのですが、パーサについてはWiki.pm側ではローカル変数として使っているだけなので、メモリリークの原因にはならないいと思うのですがあってます?プラグインからパーサを参照する部分がありますね。プラグインは一度インスタンスが生成されるとWiki.pmのインスタンス変数としてキャッシュされるので、この部分で循環参照が発生するようです。 - たけぞう (2004年04月02日 13時16分31秒)

プラグインのインスタンスをWiki.pmがキャッシュしている分には、最後にまとめてundefすることでなんとかなりそうですが、むしろ&Wiki::process_plugin (3.5.3dev5のWiki.pm,l.594)で、局所化されたプラグインインスタンスにPaserインスタンスを持たせている部分が解放を困難にしているように思います。

この部分の循環参照(プラグインインスタンス内のPaserインスタンス内のWikiインスタンス)は、&Wiki::process_pluginのスコープを抜けた時点で解放できなくなってしまいます。結果をreturnする前に

undef( $obj->{parser}->{interwiki} );
undef( $obj->{parser}->{keyword}   );

する必要があり、さらに$obj->{parser}->{wiki}をundefしないとなりませんが、それをしてしまうと、その後のパースができなくなる場合があったので結局&HTMLParser::end_parse内で

$self->{wiki} = undef;

をして、やっとWikiのデストラクタを呼び出せました(ちょっと、ここ曖昧です。今、mod_perl環境がないもので確認ができません。しかもこれだと、再度このHTMLParserインスタンスでパースしようとするとエラーになりますね)。(以上、3.5.3dev5にて)

  • ところで、循環参照を別にしても、プラグインの後処理用にhookの呼び出しがあったら嬉しいのですが、これについてはどんなものでしょう?(話題がずれてすいません) - まかまか (2004年04月03日 02時43分29秒)
  • プラグインに持たせているParserをWiki#process_pluginから抜ける前にundefすれば丸く収まると思います(おそらく…)。Wiki#exitメソッドを新たに追加しexit関数の代わりに使うようにしようと思います。また、finalizeフックも追加します。ただ、このような実装だとエラーで落ちた場合にはどちらにしても終了処理は行われませんね。 - たけぞう (2004年04月04日 02時38分14秒)
  • フックの追加ありがとうございます。エラー時に処理されないことについては、CGI::Carpの処理に割り込んで何とかならないものかと試しているのですが、何故かうまくいきません(仮にできたとしても美しくない処理ですよね……)。 - まかまか (2004年04月05日 23時11分13秒)

プラグインに持たせているParserをWiki#process_pluginから抜ける前にundef

という件ですが、実際にそれを行うとParserインスタンスが抱えているWikiインスタンスを根本から絶ってしまうようなのです。その結果、インラインプラグインなどのあるページを呼び出すと

Can't call method "config" on an undefined value
at lib/Wiki/HTMLParser.pm line 310.

などとなるように、別のところでwikiインスタンスを利用できなくなる場合が発生します(どういう場合に生じるのか、私はまだよくわかっていません)。

確実なのは、リファレンスのカタログをつくって

*** Wiki.pm.org Thu Mar 11 23:39:16 2004
--- Wiki.pm     Mon Apr 05 23:01:02 2004
***************
*** 592,597 ****
--- 592,598 ----
        } else {
                if($info->{FORMAT} eq "WIKI"){
                        $obj->{parser} = $parser; # 裏技用
+                       push @{ $self->{parser_obj} }, $obj->{parser};
                        my $cache = $parser->{cache};
                        $parser->{cache} = 0;
                        if($info->{TYPE} eq "inline"){

後処理メソッド内で

for my $parser ( @{$self->{parser_obj}} ){
	undef( $parser->{wiki} );
	undef( $parser->{interwiki} );
	undef( $parser->{keyword} );
	undef( $parser );
}

てな感じなのでしょうか(正常に動作しました)。これまた見苦しい処理ですが。

  • process_plugin内で$plugin->{parser}をundefするように修正してみたのですが、動いてますよ。こんな感じです。なにか間違ってるでしょうか。 - たけぞう (2004年04月06日 11時19分54秒)
594c594
<  $obj->{parser} = $parser; # 裏技用
---
>  $obj->{parser} = $parser; # 裏技用(プラグイン内部からパーサを使う場合)
599a600
>    undef($obj->{parser}); # パーサの参照を解放
603a605
>    undef($obj->{parser}); # パーサの参照を解放
  • やや、確かにおっしゃる通りでした。どうも$obj->{parser}->{wiki}の方にばかり目がいってしまっていました。お手数をおかけして申し訳ありません。それから以下のような方法でdieした場合で後処理が可能でした。 - まかまか (2004年04月06日 17時12分08秒)
# wiki.cgi 3.5.3dev5
25c25
< use CGI::Carp qw(fatalsToBrowser);
---
> #use CGI::Carp qw(fatalsToBrowser);
31a32,45
> BEGIN{
> 	use CGI::Carp qw(fatalsToBrowser);
> 	my $cgi_carp_die_org = \&CGI::Carp::die;
> 
> 	sub _die{
> 		my $wiki = $CGI::Carp::wiki;
> 		$wiki->process_before_exit if($wiki); # 後処理
> 		$cgi_carp_die_org->(@_);
> 	};
> 
> 	*CORE::GLOBAL::die = \&_die;
> 	$SIG{__DIE__}      = \&_die;
> }
> 
43a58
> local($CGI::Carp::wiki) = $wiki;
  • ありがとうございます。上記のパッチも取り込ませていただきます。 - たけぞう (2004年04月07日 11時38分14秒)
お名前: コメント:

最終更新時間:2006年07月18日 13時09分16秒