Nasty side-effects of Exception::Class

IlyaM on 2003-03-03T10:52:59

What happens if you combine a Perl module which heavily relies on Perl's garbage collector and reference counting to manage resources it creates (like POE) and exception objects created with Exception::Class? You end up with a web server which doesn't close sockets after serving a HTTP request :( It took hours to debug this problem. In essence this test case demonstrates the problem with Exception::Class:

use Exception::Class ('MyException');

sub test { MyException->throw(error => 'Test'); }

eval { my $x = bless {}, 'X'; test($x); }; if($@) { print 2; }

sub X::DESTROY { print 1; }
You would expect it to print '12' but it prints '21'. The problem is that Exception::Class stores full stack trace in exception object including all arguments passed to subs. I.e. it creates additional reference on them what means their destructors will not be called until your undefine or change $@. Any ideas how to fix/workaround it?


Ask MattS

Dom2 on 2003-03-03T12:55:01

He discovered a very similiar thing with Error.pm a while back. It's documented in a set of slides he did about exceptions...

Basically, closures + exceptions are a bad thing. This needs to be documented better somewhere in Perl.

-Dom

Re:Ask MattS

IlyaM on 2003-03-03T13:50:29

Exception::Class doesn't use closures and this problem is very different. The problem is that it stores Devel::StackTrace object in exceptions. And Devel::StackTrace references all arguments from all frames in stack trace. In this test case stack trace has a frame with call to test() sub which is passed $x variable so value of $x gets stored deeply somewhere in exception object.

Re:Ask MattS

Dom2 on 2003-03-03T14:10:53

Apologies; I should have looked at the article closer.

-Dom

A quick work around...

Matts on 2003-03-03T13:05:26

{
  local $@;
  eval {
    my $x = Object->new();
    do_something_bad($x);
  };
  if ($@) {
    ...
  }
}

Re:A quick work around...

IlyaM on 2003-03-03T13:43:59

Explicitly undefining $@ helps in my POE application but I can imaging a couple scenarious when it will not help. For example if you take my test case it still prints "21" instead of "12".

Real fix is probably using weak refs to store args in stack trace or don't store store them at all. After all Exception::Class needs stack trace only to print error message for uncaught exceptions. It could just generate error message at the moment when exceptions is raised and do not store stack trace in exception objects.

Ahem, it's in the docs

autarch on 2003-03-03T18:59:47

Exception::Class::Base->NoRefs(1);

This will pass along the no_refs parameter to all Devel::StackTrace objects created, and no references will be stored.

This should perhaps be the default, though.

-dave

Re:Ahem, it's in the docs

IlyaM on 2003-03-04T09:04:08

D'oh, I can't belive I didn't notice it in docs.

Making it default is definetely good idea.