Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Race condition with Mail::Sender::MailMsg?

by DrewP (Initiate)
on Feb 15, 2012 at 12:27 UTC ( #953929=perlquestion: print w/replies, xml ) Need Help??
DrewP has asked for the wisdom of the Perl Monks concerning the following question:


After some months of testing I *think* the reason why my script hangs is due to race condition in Mail::Sender::MailMsg

My diagnositic messages suggest that if my internet connection goes down while executing MailMsg() my script never returns from MailMsg

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

As this is not the kind of problem I can readily duplicate I won't try to make a minimal script to demonstrate it, but will insert my current code in case it is useful. Hopefully the variable names are self-explanatory.

(This is for a network monitoring application which sends me email when it sees changes to various variables including the external ip address of my ADSL router. Normally if the internet connection is down new Mail::Sender() fails with an appropriate error. However, occasionally the sender object is created OK but it seems that MailMsg() doesn't return. For many months I have experienced my script hanging very occasionally and always about the same time the router's external ip address changes, although usually it handles an ip address change with no problem)

I HAVE tried a trivial piece of test code which does new Mail::Sender() then sleeps for 20 seconds before executing MailMsg() during which period I bring down the adsl connection at the router. However that works satisfactorally as MailMsg produces error -3

Error sending mail: Error code -3 (connect() failed: Invalid argument)

Code which was running when the script hung this morning:

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 =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_NOTIFY +_MAILS)); if ((defined $arg_ref->{file}) && (-f $arg_ref->{file})) { # Sen +d message with attachment print "sending mail with attachment\n" if ($onterm && ($debug & $DEB +UG_NOTIFY_MAILS)); $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} }); if (defined $sender->{'error'} ){ logerror( "Error sending mail with attachment '$arg_ref->{file}' t +o $arg_ref->{to}: Error code $sender->{'error'} ($sender->{'error_msg +'})"); $sendfailed = 1; } else { loginfo("File \"$arg_ref->{file}\" successfully emailed to $arg_re +f->{to}"); } } else { #send a message with no attachment $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)); $sender->MailMsg({to => $arg_ref->{to}, subject => $arg_ref->{subject}, encoding => "Quoted-printable", ## read this from paramete +rs? msg => encode($charset,$arg_ref->{msg}), }); if (defined $sender->{'error'} ){ logerror( "Error sending mail to $arg_ref->{to}: Error code $sende +r->{'error'} ($sender->{'error_msg'})"); $sendfailed = 1; } } } # if ($sender > 0) else { logerror("Could not establish mail sender to $arg_ref->{to}: Error + code $sender ($Mail::Sender::Error)"); $sendfailed = 1; } return ($sendfailed) ? 0 : 1; }

When it failed this morning the last lines sent to the terminal window were:

Running MailChangeNotifications Running sendmail() $sender created OK sending mail without attachment

I realized when composing this post that my diagnostics do not log the successful exit of MailMsg, and I have modified my production code to do so in future, but I believe I am justified in suspecting MailMsg because:

  • I don't receive the email
  • The code that follows this subroutine call emits similar diagnostic messages which I would expect to see if this subroutine exited successfully
  • It may be several weeks before I see a similar failure

Thanks in advance to anyone who can help...

Replies are listed 'Best First'.
Re: Race condition with Mail::Sender::MailMsg?
by Marshall (Abbot) on Feb 15, 2012 at 13:18 UTC
    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.

      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.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://953929]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (5)
As of 2018-07-18 09:15 GMT
Find Nodes?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

    Results (388 votes). Check out past polls.