in reply to Race condition with Mail::Sender::MailMsg?

Is there any way of enforcing a timeout on MailMsg? (And also MailFile?)

The general way to implement a "time-out" is to install an ALRM signal handler, then set the alarm function with alarm(NumOfSeconds).

$SIG{ALRM} = sub {print "oh darn, timeout!; die "timeout";} alarm(10); #10 second "countdown" somefunction(); alarm(0); #disables alarm # only get here if less than 10 seconds elapsed # during somefunction()
There are a WHOLE mess of caveats and "yeah but's" with alarms. So this is a complex subject! One common problem on Windows is that sleep() is implemented in terms of ALRM and this can cause some weird side-effects. But I assume that you are on Unix.

Perl >= 7.3 implements what are called "safe" or deferred signals which means that it doesn't handle them right away by default. Good news is that the Perl program can continue after an ALRM like this. Bad news is that in some cases the alarm signal might not be delivered.

Read this: cpan: Deferred Signals.
also, Perl doc alarm

I am sure that other Monks will suggest other references.
Try a normal "safe" mode alarm first using an eval block. That will allow your program to continue operation and try the function again. If that doesn't work and you need "non-safe" mode operation, then you must do as little as possible and exit() as soon as possible after the alarm because a low level non-reentrant OS function may have been interrupted.

Replies are listed 'Best First'.
Re^2: Race condition with Mail::Sender::MailMsg?
by DrewP (Initiate) on Feb 15, 2012 at 15:08 UTC
    Thanks, Marshall.

    As suggested I have done 'safe' mode with an eval block, and the script is sending me mails as it should.

    Now I just have to leave it running for several weeks to see if it logs a timeout and re-tries later as it should, or if it falls over and dies horribly!

    I am very pleased and impressed to have had such a rapid response. I can't bring myself to learn python and I was worrying that I was the only person who still uses perl.

    Thanks again - DrewP

      Debugging these "happens every few weeks" problems are hard!

      I am glad to hear that you are aware of the issues and "are on the way" - the journey may be long, but I'm glad to hear that my suggestion was understandable, implementable and hopefully ultimately helpful to you!

      Yeah, so show me the level of control that you've seen about Perl signals in Python! HA!
      Phython vs Perl isn't like tricycles vs motorcycles, it is more like tricycles vs jet airplanes.

        Just to confirm the suggested solution works. Today, just at the time the internet connection went down, my script logged the expiry of a 20 second timeout sending a simple message but carried on running.

        Of course it's easy for me because I know what sort of messages I will be sending and can thus set appropriate timeouts. As a general solution the calculation of the appropriate timeout becomes more complicated.

        In case it is of interest to anyone I have put my modified code below.

        sub sendmail { my ($arg_ref) = @_; print "Running sendmail()\n" if ($onterm && ($debug & $DEBUG_NOTIFY_ +MAILS)); =pod This subroutine is the interface to the mail sender functionality. Depending on incoming parameters it sends just a message or a message + with attachment If the attachment file does not exist it sends message with notificat +ion that file was not found =cut my $sendfailed = 0; my $sender = new Mail::Sender( {from => $arg_ref->{from}, smtp => $arg_ref->{smtp}, port => $arg_ref->{port}, auth => $arg_ref->{auth}, authid => $arg_ref->{authid}, authpwd => $arg_ref->{authpwd}, auth_encoded => $arg_ref->{auth_encoded}, encoding => 'Quoted-printable', charset => $charset, }); if ($sender > 0) { print "\$sender created OK\n" if ($onterm && ($debug & $DEBUG_NOTI +FY_MAILS)); if ((defined $arg_ref->{file}) && (-f $arg_ref->{file})) { # Sen +d message with attachment if the attached file exists print "sending mail with attachment\n" if ($onterm && ($debug & +$DEBUG_NOTIFY_MAILS)); my $mailfiletimeout = 120; #120 seconds eval { local $SIG{ALRM} = sub { die "MailFile Timeout\n" }; # NB: \n requ +ired alarm $mailfiletimeout; $sender->MailFile({to => $arg_ref->{to}, subject => $arg_ref->{subject}, encoding => "Base64", ## read this from parameters? msg => encode($charset,$arg_ref->{msg}), file => $arg_ref->{file} }); alarm 0; }; if ($@) { die unless $@ eq "MailFile Timeout\n"; # propagate unexpected erro +rs logerror("Timeout ($mailfiletimeout seconds) sending $arg_ref->{fi +le} to $arg_ref->{to} via $arg_ref->{smtp}"); return 0; } else { if (defined $sender->{'error'} ){ logerror( "Error sending mail with attachment '$arg_ref->{file}' + to $arg_ref->{to}: Error code $sender->{'error'} ($sender->{'error_m +sg'})"); $sendfailed = 1; } else { loginfo("File \"$arg_ref->{file}\" successfully emailed to $arg_ +ref->{to}"); } } } else { #send a message with no attachment #Should never receive a filename for a file that does not exist. + Add a line to the message if it ever happens $arg_ref->{msg} .= "\n\nAttempt to send file '$arg_ref->{file}' + abandoned: File does not exist" if defined $arg_ref->{file}; print "sending mail without attachment\n" if ($onterm && ($debug + & $DEBUG_NOTIFY_MAILS)); my $mailmessagetimeout = 20; #20 seconds eval { local $SIG{ALRM} = sub { die "MailMsg Timeout\n" }; # NB: \n requi +red alarm $mailmessagetimeout; $sender->MailMsg({to => $arg_ref->{to}, subject => $arg_ref->{subject}, encoding => "Quoted-printable", ## read this from parame +ters? msg => encode($charset,$arg_ref->{msg}), }); alarm 0; }; if ($@) { die unless $@ eq "MailMsg Timeout\n"; # propagate unexpected error +s logerror("Timeout ($mailmessagetimeout seconds) sending message to + $arg_ref->{to} via $arg_ref->{smtp}"); return 0; } else { if (defined $sender->{'error'} ){ logerror( "Error sending mail to $arg_ref->{to}: Error code $sen +der->{'error'} ($sender->{'error_msg'})"); $sendfailed = 1; } else { print "Mail sent to $arg_ref->{to}\n" if ($onterm && ($debug & + $DEBUG_NOTIFY_MAILS)); } } } # send a message with no attachment } # if ($sender > 0) else { logerror("Could not establish mail sender to $arg_ref->{to}: Error + code $sender ($Mail::Sender::Error)"); $sendfailed = 1; } print "Leaving sendmail (\$sendfailed = $sendfailed)\n" if ($onterm + && ($debug & $DEBUG_NOTIFY_MAILS)); return ($sendfailed) ? 0 : 1; }