Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.85k stars 524 forks source link

should goto &NAME free lexicals? #7118

Closed p5pRT closed 20 years ago

p5pRT commented 20 years ago

Migrated from rt.perl.org#26959 (status was 'resolved')

Searchable as RT26959$

p5pRT commented 20 years ago

From sorourke@cs.ucsd.edu

This is a bug report for perl from seano@​cs.ucsd.edu\, generated with the help of perlbug 1.34 running under perl v5.8.3.

From the description in perlfunc\, I thought you could use "goto-&FUNC" for tail call elimination. However\, it looks like lexicals don't get freed. Try the following three programs​:

perl -e 'sub foo { my $x = shift; print $x unless $x%100000; foo($x-1) } foo(1);'

perl -e 'sub foo { my $x = shift; print $x unless $x%100000; @​_=($x-1);goto &foo }'

perl -e 'sub foo { print $_[0] unless $_[0]%100000; $_[0]--;goto &foo } foo($x=1)'

With 5.8.3 on OS X\, the first (of course) quickly grows out of control\, while the third does not. The second also grows out of control\, though more slowly than the first. My guess is that while the stack isn't growing\, lexicals and temps are still not being freed. Am I misusing goto?


Flags​:   category=core   severity=low


Site configuration information for perl v5.8.3​:

Configured by seanorourke at Sun Jan 25 10​:48​:24 PST 2004.

Summary of my perl5 (revision 5.0 version 8 subversion 3) configuration​:   Platform​:   osname=darwin\, osvers=6.8\, archname=darwin-thread-multi   uname='darwin rh220-50.resnet.ucsd.edu 6.8 darwin kernel version 6.8​: wed sep 10 15​:20​:55 pdt 2003; root​:xnuxnu-344.49.obj~2release_ppc power macintosh powerpc '   config_args=''   hint=previous\, useposix=true\, d_sigaction=define   usethreads=define use5005threads=undef useithreads=define usemultiplicity=define   useperlio=define d_sfio=undef uselargefiles=define usesocks=undef   use64bitint=undef use64bitall=undef uselongdouble=undef   usemymalloc=n\, bincompat5005=undef   Compiler​:   cc='cc'\, ccflags ='-pipe -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -I/usr/local/include -I/opt/local/include -I/sw/include'\,   optimize='-O3'\,   cppflags='-no-cpp-precomp -pipe -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -I/usr/local/include -I/opt/local/include -I/sw/include -pipe -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -I/usr/local/include -I/opt/local/include -I/sw/include'   ccversion=''\, gccversion='3.3 20030304 (Apple Computer\, Inc. build 1435)'\, gccosandvers=''   intsize=4\, longsize=4\, ptrsize=4\, doublesize=8\, byteorder=4321   d_longlong=define\, longlongsize=8\, d_longdbl=define\, longdblsize=8   ivtype='long'\, ivsize=4\, nvtype='double'\, nvsize=8\, Off_t='off_t'\, lseeksize=8   alignbytes=8\, prototype=define   Linker and Libraries​:   ld='cc'\, ldflags ='-flat_namespace -L/usr/local/lib -L/opt/local/lib -L/sw/lib'   libpth=/usr/local/lib /opt/local/lib /usr/lib /sw/lib   libs=-lgdbm -ldl -lm -lc   perllibs=-ldl -lm -lc   libc=/usr/lib/libc.dylib\, so=dylib\, useshrplib=false\, libperl=libperl.a   gnulibc_version=''   Dynamic Linking​:   dlsrc=dl_dyld.xs\, dlext=bundle\, d_dlsymun=undef\, ccdlflags=' '   cccdlflags='-flat_namespace -bundle -fPIC'\, lddlflags=' -flat_namespace -bundle -undefined suppress -L/usr/local/lib -L/opt/local/lib -L/sw/lib'

Locally applied patches​:  


@​INC for perl v5.8.3​:   /opt/perl/lib/5.8.0   /opt/perl/lib/5.8.0/darwin   /opt/perl/lib/site_perl   /opt/perl/lib/site_perl/5.8.0   /opt/perl/lib/site_perl/5.8.0/darwin   /usr/local/lib/perl5/5.8.3/darwin-thread-multi   /usr/local/lib/perl5/5.8.3   /usr/local/lib/perl5/site_perl/5.8.3/darwin-thread-multi   /usr/local/lib/perl5/site_perl/5.8.3   /usr/local/lib/perl5/site_perl   /opt/perl/lib/5.8.0   /opt/perl/lib/site_perl/5.8.0   .


Environment for perl v5.8.3​:   DYLD_LIBRARY_PATH (unset)   HOME=/Users/seanorourke   LANG (unset)   LANGUAGE (unset)   LD_LIBRARY_PATH=/System/Library/Frameworks/JavaVM.framework/Versions/1.4.1/Libraries​:/Users/seanorourke/lib​:/usr/local/lib​:/sw/lib   LOGDIR (unset)   PATH=/Users/seanorourke/bin​:/usr/local/bin​:/opt/local/bin​:/usr/sbin​:/sbin​:/sw/bin​:/sw/sbin​:/Users/seanorourke/bin​:/usr/local/bin​:/opt/local/bin​:/usr/sbin​:/sbin​:/sw/bin​:/sw/sbin​:/usr/bin​:/bin​:/sw/bin​:/usr/local/bin​:/usr/local/teTeX/bin/powerpc-apple-darwin-current​:/usr/X11R6/bin​:/usr/X11R6/bin   PERLLIB=/opt/perl/lib/5.8.0​:/opt/perl/lib/5.8.0/darwin​:/opt/perl/lib/site_perl​:/opt/perl/lib/site_perl/5.8.0​:/opt/perl/lib/site_perl/5.8.0/darwin   PERL_BADLANG (unset)   PERL_INLINE_JAVA_JNI=1   SHELL=/usr/local/bin/zsh

p5pRT commented 20 years ago

From whatever@davidnicol.com

the C\<goto &NAME> construct is not for tail recursion\, but is for fooling the C\ function.

I hope someone will correct me if I'm wrong\, but I don't think Perl has tail-recursion optimization. You can nonetheless fake it by keeping your variables in an object and calling a series of methods on the object instead of recursing\, or returning to a central dispatcher to keep the stack small\, as in the following code.

#!/usr/bin/perl { # fake tail recursion demonstration   my $wheretonext = "foo";

  sub foo_worker{ # in a real system there would be several of these   my $x = shift;   print "$x\n" unless $x % 100000;   $wheretonext = "foo";   return 1+$x;   };

  sub call_my_foo{ # the dispatch routine

  $nextval = 1;

  for(;;){   $nextval = &{"${wheretonext}_worker"}($nextval);   };   }  
} call_my_foo();

__END__

That will run forever and not grow.

On Fri\, 2004-02-20 at 12​:37\, Sean O'Rourke wrote​:

From the description in perlfunc\, I thought you could use "goto-&FUNC" for tail call elimination. However\, it looks like lexicals don't get freed.

p5pRT commented 20 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 20 years ago

From @iabyn

On Tue\, Feb 24\, 2004 at 12​:00​:22AM -0600\, david nicol wrote​:

the C\<goto &NAME> construct is not for tail recursion\, but is for fooling the C\ function.

I hope someone will correct me if I'm wrong\, but I don't think Perl has tail-recursion optimization.

Well\, C\<goto &foo> is deliberatley designed to pop the current call off the call stack and push a new one\, so the the reported bug is genuine. And C\<goto &foo> is intended as a poor-man's tail-recursion optimization.

-- There's a traditional definition of a shyster​: a lawyer who\, when the law is against him\, pounds on the facts; when the facts are against him\, pounds on the law; and when both the facts and the law are against him\, pounds on the table.   -- Eben Moglen referring to SCO

p5pRT commented 20 years ago

From whatever@davidnicol.com

On Tue\, 2004-02-24 at 03​:23\, Dave Mitchell wrote​:

On Tue\, Feb 24\, 2004 at 12​:00​:22AM -0600\, david nicol wrote​:

the C\<goto &NAME> construct is not for tail recursion\, but is for fooling the C\ function.

I hope someone will correct me if I'm wrong\, but I don't think Perl has tail-recursion optimization.

Well\, C\<goto &foo> is deliberatley designed to pop the current call off the call stack and push a new one\, so the the reported bug is genuine. And C\<goto &foo> is intended as a poor-man's tail-recursion optimization.

Then it isn't just lexicals that aren't getting cleaned up​:

  perl -le '$x = 1; sub f(){ ++$x % 10000 or print $x; goto &f}; f'

leaks too.

-- david nicol   War is hell.

p5pRT commented 20 years ago

From @iabyn

On Fri\, Feb 20\, 2004 at 06​:37​:39PM -0000\, Sean O'Rourke wrote​:

perl -e 'sub foo { my $x = shift; print $x unless $x%100000; @​_=($x-1);goto &foo }'

The leak is tiggered by the assignment to @​_. This causes a copy of the AV holding @​_ to be left on the tmpstack. On each goto\, the tmpstack grows by one\, and another AV is leaked.

Fixed by the following change.

Dave.

-- To collect all the latest movies\, simply place an unprotected ftp server on the Internet\, and wait for the disk to fill....

Change 22373 by davem@​davem-percy on 2004/02/24 23​:25​:52

  [perl #26959] fix memory leak in @​_ = ...; goto &sub

Affected files ...

... //depot/perl/pp_ctl.c#384 edit

Differences ...

==== //depot/perl/pp_ctl.c#384 (text) ====

@​@​ -2182\,6 +2182\,7 @​@​   char *label;   int do_dump = (PL_op->op_type == OP_DUMP);   static char must_have_label[] = "goto must have label"; + AV *oldav = Nullav;

  label = 0;   if (PL_op->op_flags & OPf_STACKED) { @​@​ -2242\,7 +2243\,7 @​@​   GvAV(PL_defgv) = cx->blk_sub.savearray;   /* abandon @​_ if it got reified */   if (AvREAL(av)) { - (void)sv_2mortal((SV*)av); /* delay until return */ + oldav = av; /* delay until return */   av = newAV();   av_extend(av\, items-1);   AvFLAGS(av) = AVf_REIFY; @​@​ -2268\,6 +2269\,9 @​@​

  /* Now do some callish stuff. */   SAVETMPS; + /* For reified @​_\, delay freeing till return from new sub */ + if (oldav) + SAVEFREESV((SV*)oldav);   SAVEFREESV(cv); /* later\, undo the 'avoid premature free' hack */   if (CvXSUB(cv)) { #ifdef PERL_XSUB_OLDSTYLE

p5pRT commented 20 years ago

From @iabyn

On Tue\, Feb 24\, 2004 at 04​:14​:47AM -0600\, david nicol wrote​:

On Tue\, 2004-02-24 at 03​:23\, Dave Mitchell wrote​:

On Tue\, Feb 24\, 2004 at 12​:00​:22AM -0600\, david nicol wrote​:

the C\<goto &NAME> construct is not for tail recursion\, but is for fooling the C\ function.

I hope someone will correct me if I'm wrong\, but I don't think Perl has tail-recursion optimization.

Well\, C\<goto &foo> is deliberatley designed to pop the current call off the call stack and push a new one\, so the the reported bug is genuine. And C\<goto &foo> is intended as a poor-man's tail-recursion optimization.

Then it isn't just lexicals that aren't getting cleaned up​:

perl -le '$x = 1; sub f(){ ++$x % 10000 or print $x; goto &f}; f'

leaks too.

only prior to 5.8.1

-- My get-up-and-go just got up and went.

p5pRT commented 20 years ago

@iabyn - Status changed from 'open' to 'resolved'