strange side-effect (bug?) in Net::SMTP

I

Ian D.

This is perl, version 5.005_02 built for sun4-solaris

I have an array that is wiped out when calling Net::SMTP->new. Here
is a debug from it. Note my array is intact at line 356, but after a
'next', it is gone:

main::check_mailservers(./conchk:356):
356: $smtp = Net::SMTP->new('ondar',Timeout=>10,Debug=>0);
DB<2> x @MAILSERVERS
0 ARRAY(0x6ea244)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<3> n

main::check_mailservers(./conchk:357):
357: $td=tv_interval($t0);
DB<3> x @MAILSERVERS
0 undef <======= WHAT THE HECK JUST HAPPENED???

A bug?

Ian
 
B

Bob Walton

Ian said:
This is perl, version 5.005_02 built for sun4-solaris

I have an array that is wiped out when calling Net::SMTP->new. Here
is a debug from it. Note my array is intact at line 356, but after a
'next', it is gone:

main::check_mailservers(./conchk:356):
356: $smtp = Net::SMTP->new('ondar',Timeout=>10,Debug=>0);
DB<2> x @MAILSERVERS
0 ARRAY(0x6ea244)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150


So, @MAILSERVERS is a one-element array, the value of which is a
reference to another array with 5 elements. The first four of those
elements have values which are various strings, and the fifth has a
value which is an integer.

DB<3> n

main::check_mailservers(./conchk:357):
357: $td=tv_interval($t0);
DB<3> x @MAILSERVERS
0 undef <======= WHAT THE HECK JUST HAPPENED???


And now @MAILSERVERS is still a one-element array, but the value of it
is now undef, rather than an array reference. This behavior could be
explained if junk450.pl is the following program:

D:\junk>type junk450.pl
$MAILSERVERS[0]=[
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150,
];
for $smtp (@MAILSERVERS){
$smtp=undef;
}
--------------------------------
and it is stepped through as follows:
--------------------------------

D:\junk>perl -d junk450.pl

Loading DB routines from perl5db.pl version 1.19
Editor support available.

Enter h or `h h' for help, or `perldoc perldebug' for more help.

main::(junk450.pl:1): $MAILSERVERS[0]=[
main::(junk450.pl:2): 'sendmail',
main::(junk450.pl:3): 'ondar',
main::(junk450.pl:4): '(e-mail address removed)',
main::(junk450.pl:5): 'nobody@localhost',
main::(junk450.pl:6): 150,
main::(junk450.pl:7): ];
DB<1> s
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<1> s
main::(junk450.pl:9): $smtp=undef;
DB<1> x @MAILSERVERS
0 ARRAY(0x1558798)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<2> n
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<2> x @MAILSERVERS
0 undef
DB<3>


Note the debug output which is identical to yours (except of course for
the address of the anonymous array and the line numbers).



Yes, in your code.
 
I

Ian D.

Bob Walton said:
Ian said:
This is perl, version 5.005_02 built for sun4-solaris

I have an array that is wiped out when calling Net::SMTP->new. Here
is a debug from it. Note my array is intact at line 356, but after a
'next', it is gone:

main::check_mailservers(./conchk:356):
356: $smtp = Net::SMTP->new('ondar',Timeout=>10,Debug=>0);
DB<2> x @MAILSERVERS
0 ARRAY(0x6ea244)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150


So, @MAILSERVERS is a one-element array, the value of which is a
reference to another array with 5 elements. The first four of those
elements have values which are various strings, and the fifth has a
value which is an integer.

DB<3> n

main::check_mailservers(./conchk:357):
357: $td=tv_interval($t0);
DB<3> x @MAILSERVERS
0 undef <======= WHAT THE HECK JUST HAPPENED???


And now @MAILSERVERS is still a one-element array, but the value of it
is now undef, rather than an array reference. This behavior could be
explained if junk450.pl is the following program:

D:\junk>type junk450.pl
$MAILSERVERS[0]=[
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150,
];
for $smtp (@MAILSERVERS){
$smtp=undef;
}
--------------------------------
and it is stepped through as follows:
--------------------------------

D:\junk>perl -d junk450.pl

Loading DB routines from perl5db.pl version 1.19
Editor support available.

Enter h or `h h' for help, or `perldoc perldebug' for more help.

main::(junk450.pl:1): $MAILSERVERS[0]=[
main::(junk450.pl:2): 'sendmail',
main::(junk450.pl:3): 'ondar',
main::(junk450.pl:4): '(e-mail address removed)',
main::(junk450.pl:5): 'nobody@localhost',
main::(junk450.pl:6): 150,
main::(junk450.pl:7): ];
DB<1> s
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<1> s
main::(junk450.pl:9): $smtp=undef;
DB<1> x @MAILSERVERS
0 ARRAY(0x1558798)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<2> n
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<2> x @MAILSERVERS
0 undef
DB<3>


Note the debug output which is identical to yours (except of course for
the address of the anonymous array and the line numbers).



Yes, in your code.



Thanks for the reply, but I don't think that is it. I don't have to
assign the new Net::SMTP object to anything, and my array is still
wiped out. Here's more debug output:

DB<7> x @MAILSERVERS
0 ARRAY(0x740594)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<8> Net::SMTP->new('ondar');
DB<9> x @MAILSERVERS
0 undef


Somehow my anonymous array at 0x740594 in this case is being
overwritten in the new() function, but I don't know why.

Ian
 
J

James Willmore

On Fri, 16 Apr 2004 06:30:23 -0700, Ian D. wrote:

[ ... ]
DB<7> x @MAILSERVERS
0 ARRAY(0x740594)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<8> Net::SMTP->new('ondar');
DB<9> x @MAILSERVERS
0 undef


Somehow my anonymous array at 0x740594 in this case is being
overwritten in the new() function, but I don't know why.

Can you provide the offending code (**NOT** the whole script, just the
portions that appear to be given you issue). I can see something is going
South, but at a loss to know why.

--
Jim

Copyright notice: all code written by the author in this post is
released under the GPL. http://www.gnu.org/licenses/gpl.txt
for more information.

a fortune quote ...
I've given up reading books; I find it takes my mind off myself.
 
I

Ian D.

Bob Walton said:
Ian said:
This is perl, version 5.005_02 built for sun4-solaris

I have an array that is wiped out when calling Net::SMTP->new. Here
is a debug from it. Note my array is intact at line 356, but after a
'next', it is gone:

main::check_mailservers(./conchk:356):
356: $smtp = Net::SMTP->new('ondar',Timeout=>10,Debug=>0);
DB<2> x @MAILSERVERS
0 ARRAY(0x6ea244)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150


So, @MAILSERVERS is a one-element array, the value of which is a
reference to another array with 5 elements. The first four of those
elements have values which are various strings, and the fifth has a
value which is an integer.

DB<3> n

main::check_mailservers(./conchk:357):
357: $td=tv_interval($t0);
DB<3> x @MAILSERVERS
0 undef <======= WHAT THE HECK JUST HAPPENED???


And now @MAILSERVERS is still a one-element array, but the value of it
is now undef, rather than an array reference. This behavior could be
explained if junk450.pl is the following program:

D:\junk>type junk450.pl
$MAILSERVERS[0]=[
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150,
];
for $smtp (@MAILSERVERS){
$smtp=undef;
}
--------------------------------
and it is stepped through as follows:
--------------------------------

D:\junk>perl -d junk450.pl

Loading DB routines from perl5db.pl version 1.19
Editor support available.

Enter h or `h h' for help, or `perldoc perldebug' for more help.

main::(junk450.pl:1): $MAILSERVERS[0]=[
main::(junk450.pl:2): 'sendmail',
main::(junk450.pl:3): 'ondar',
main::(junk450.pl:4): '(e-mail address removed)',
main::(junk450.pl:5): 'nobody@localhost',
main::(junk450.pl:6): 150,
main::(junk450.pl:7): ];
DB<1> s
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<1> s
main::(junk450.pl:9): $smtp=undef;
DB<1> x @MAILSERVERS
0 ARRAY(0x1558798)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<2> n
main::(junk450.pl:8): for $smtp (@MAILSERVERS){
DB<2> x @MAILSERVERS
0 undef
DB<3>


Note the debug output which is identical to yours (except of course for
the address of the anonymous array and the line numbers).



Yes, in your code.



Well, it looks like new() is modifying $_. I use $_ as my control
variable in a foreach loop (implicitly), each time it points to an
element of @MAILSERVERS. Net::SMTP->new() seems to change $_, and
hence changes my array.

main::check_mailservers(./conchk:357):
357: Net::SMTP->new('ondar');
DB<11> x \$_
0 SCALAR(0x79cac8)
-> ARRAY(0x79ca98)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<12> n

main::check_mailservers(./conchk:358):
358: $td=tv_interval($t0);
DB<12> x \$_
0 SCALAR(0x79cac8)
-> undef


If I use another variable to drive my loop, all is well. I don't
believe a subroutine should be modifying $_, should it?

Ian
 
B

Bob Walton

Ian said:
....


If I use another variable to drive my loop, all is well. I don't
believe a subroutine should be modifying $_, should it?

Ian


As for whether a sub can modify $_, that is easily tested:

D:\junk>type junk450b.pl
$_='something';
sub1();
print "\$_ is now $_\n";

sub sub1{
$_='something else';
}

D:\junk>perl junk450b.pl
$_ is now something else

D:\junk>

And the answer is: Yes, a sub can modify $_. $_, along with all other
"punctuation" variables, is a true global -- if modified somewhere, it
is modified everywhere. A *good* *nice* sub would avoid using $_, or if
it does use it, would preserve the value of $_ and any other globals it
might change, through the use of local(). But how many folks actually
take the time and energy to do that in their subs (or even know they
should)? And how easy is it to mess up? Better to never count on $_
being preserved over a call. But nonetheless, a good module *should*
preserve globals. So I probably owe you an apology -- the bug probably
was in Net::SMTP. But it is still counting on a lot to assume that all
the stuff involved with a complicated module will actually preserve $_,
and it is still better to not count on it. What if the module takes an
execution path the testing didn't test, for example?


Hmmmmmmmm...a test program on my system does not duplicate your
behavior. Your Perl is pretty ancient (5.005_02) -- you might try a
modern version and see if the problem has been fixed. Here is a test
program almost identical to what you posted and its output:

D:\junk>type junk450a.pl
use Net::SMTP;
use Data::Dumper;
$MAILSERVERS[0]=[
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150,
];
foreach (@MAILSERVERS) {
my($type,$server,$from,$to,$defer,$blackout)=@$_;
$statfile=$MAILSERVER_STATE . $server;
print "Connecting to $type server $server...";
$t0=[gettimeofday];
# $failed=do_smtp($server,$from,$to);
#$smtp = Net::SMTP->new('ondar');
Net::SMTP->new('ondar');
print Dumper(@MAILSERVERS);
# @MAILSERVERS is gone now
# $td=tv_interval($t0); #commented because undefined
}

D:\junk>perl junk450a.pl
Connecting to sendmail server ondar...$VAR1 = [
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150
];

D:\junk>ver

Windows 98 [Version 4.10.2222]


D:\junk>perl -v

This is perl, v5.8.0 built for MSWin32-x86-multi-thread
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2002, Larry Wall

Binary build 806 provided by ActiveState Corp. http://www.ActiveState.com
Built 00:45:44 Mar 31 2003
 
I

Ian D.

Bob Walton said:
Ian said:
...


If I use another variable to drive my loop, all is well. I don't
believe a subroutine should be modifying $_, should it?

Ian


As for whether a sub can modify $_, that is easily tested:

D:\junk>type junk450b.pl
$_='something';
sub1();
print "\$_ is now $_\n";

sub sub1{
$_='something else';
}

D:\junk>perl junk450b.pl
$_ is now something else

D:\junk>

And the answer is: Yes, a sub can modify $_. $_, along with all other
"punctuation" variables, is a true global -- if modified somewhere, it
is modified everywhere. A *good* *nice* sub would avoid using $_, or if
it does use it, would preserve the value of $_ and any other globals it
might change, through the use of local(). But how many folks actually
take the time and energy to do that in their subs (or even know they
should)? And how easy is it to mess up? Better to never count on $_
being preserved over a call. But nonetheless, a good module *should*
preserve globals. So I probably owe you an apology -- the bug probably
was in Net::SMTP. But it is still counting on a lot to assume that all
the stuff involved with a complicated module will actually preserve $_,
and it is still better to not count on it. What if the module takes an
execution path the testing didn't test, for example?


Hmmmmmmmm...a test program on my system does not duplicate your
behavior. Your Perl is pretty ancient (5.005_02) -- you might try a
modern version and see if the problem has been fixed. Here is a test
program almost identical to what you posted and its output:

D:\junk>type junk450a.pl
use Net::SMTP;
use Data::Dumper;
$MAILSERVERS[0]=[
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150,
];
foreach (@MAILSERVERS) {
my($type,$server,$from,$to,$defer,$blackout)=@$_;
$statfile=$MAILSERVER_STATE . $server;
print "Connecting to $type server $server...";
$t0=[gettimeofday];
# $failed=do_smtp($server,$from,$to);
#$smtp = Net::SMTP->new('ondar');
Net::SMTP->new('ondar');
print Dumper(@MAILSERVERS);
# @MAILSERVERS is gone now
# $td=tv_interval($t0); #commented because undefined
}

D:\junk>perl junk450a.pl
Connecting to sendmail server ondar...$VAR1 = [
'sendmail',
'ondar',
'(e-mail address removed)',
'nobody@localhost',
150
];

D:\junk>ver

Windows 98 [Version 4.10.2222]


D:\junk>perl -v

This is perl, v5.8.0 built for MSWin32-x86-multi-thread
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2002, Larry Wall

Binary build 806 provided by ActiveState Corp. http://www.ActiveState.com
Built 00:45:44 Mar 31 2003


I agree that you shouldn't count on $_ across function calls, and I
have fixed that. As for the version of perl, I did try my code on a
newer version and did not have the same problem. So I really had 2
ways to fix the problem. The easiest, of course, was to just change
my control variable. Apology accepted. :)

Ian
 
J

James Willmore

Thanks for the reply, but I don't think that is it. I don't have to
assign the new Net::SMTP object to anything, and my array is still
wiped out. Here's more debug output:

DB<7> x @MAILSERVERS
0 ARRAY(0x740594)
0 'sendmail'
1 'ondar'
2 '(e-mail address removed)'
3 'nobody@localhost'
4 150
DB<8> Net::SMTP->new('ondar');
DB<9> x @MAILSERVERS
0 undef


Somehow my anonymous array at 0x740594 in this case is being
overwritten in the new() function, but I don't know why.

Can you provide the offending code (**NOT** the whole script, just the
portions that appear to be given you issue). I can see something is going
South, but at a loss to know why.

--
Jim

Copyright notice: all code written by the author in this post is
released under the GPL. http://www.gnu.org/licenses/gpl.txt
for more information.

a fortune quote ...
I've given up reading books; I find it takes my mind off myself.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top