A die() occurring within a forked child process will get caught by a containing eval block in the parent that expects to happen only once. Normally, one might expect a programmer to account for a fork() in his error handling. However, since a number of commonly-used CPAN modules use fork() or other forking (e.g. via IPC::OpenX or the open("|-") idiom) without mentioning this in the docs, it is easy to get bitten. Did I miss the memo that says we have to check whether a new process was spawned after every eval block?

While implementing a multistep database transaction, I decided to use an idiom I learned in Java: begin transaction , try/catch block, and either commit or rollback based upon the presence of an exception.

In Perl, it was roughly:

eval { $dbh->begin(); # stuff... $mime_entity_instance->send(); } if ($@) { $dbh->rollback(); } else { $dbh->commit(); }

The MIME::Entity instance is a multipart email message, and uses Mail::Mailer to send itself. Unbeknownst to me, Mail::Mailer forks a child process, and when I used taint mode, the PATH was dirty and caused the Mail::Mailer child process exec() to fail. I had a hell of a time figuring out why my code was trying to both commit and rollback.

I've submitted a patch to Mail::Mailer, at http://rt.cpan.org/NoAuth/Bug.html?id=12890

While I can locally solve this in a number of ways, the larger question this brings up is if it is EVER OK to call outside code within an eval block. I can't imagine that everybody, everywhere, checks process IDs before continuing after an eval failure.

You can reproduce the general problem yourself:

#!/usr/bin/perl -w use strict; my $num = '123'; # an external database ID, for example warn "(PID $$) Beginning transaction #$num "; eval { my $pid = fork(); if ($pid) { warn "\t(the parent does OK)"; } else { warn "\t(the child fails)"; die "Child process failure"; } }; if ($@) { warn "(PID $$) Rolling back transaction #$num: died in eval with $ +@"; } else { warn "(PID $$) Committing transaction #$num"; }

Thoughts from the monks? How generally should one deal with this problem?

Update: Grr, I thought my patch had solved the problem -- however, if you are using it in a database environment, the fact that the code exits in the child seems to be calling DESTROY on objects that have external effects (e.g. turning AutoCommit back on in the database). So, problem NOT solved for those of you looking to use Mail::Mailer and derivatives in a transactional eval block.


In reply to forked die() in eval block has counterintuitive behavior? by rlucas

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.