develooper Front page | perl.perl5.porters | Postings from December 2011

[perl #106536] PL_last_in_gv paradigm conflict

Father Chrysostomos
December 18, 2011 19:48
[perl #106536] PL_last_in_gv paradigm conflict
Message ID:
# New Ticket Created by  Father Chrysostomos 
# Please include the string:  [perl #106536]
# in the subject line of all future correspondence about this issue. 
# <URL: >


PL_last_in_gv is where perl remembers the last-accessed filehandle.  This is what $. and tell and eof without arguments use.  It is set by readline, tell, eof and seek.

This variable is not reference-counted, so when a gv is freed PL_last_in_gv is set to null if it points to it.

As of perl 5.14, rv2gv (*{...}) returns a mortal copy of the glob, with the FAKE flag off, that shares the same glob pointer.

(The FAKE flag on a glob means that assignment to it will clobber it.  *foo has the FAKE flag off.  my $fh = *foo results in $fh being a glob with the FAKE flag on.)

tell, eof and seek all supply an implicit *{...} around their (first) argument.  readline, oddly enough, does not.

Before I fixed it just recently, PL_last_in_gv could point to a freed scalar as a result of:

   $f{g} = *foo;
   $f{g} = 3;
   undef %h;

The $f{g} = 3 assignment would cause PL_last_in_gv to cease being a glob.  undef %h would free the scalar in PL_last_in_gv.  Since it was the glob-freeing code that cleared PL_last_in_gv, it would not get called when the scalar was freed, because it was no longer a glob.  I fixed this by making glob coercion (during the $f{g} = 3 assignment) also unset PL_last_in_gv.

There was also a regression in 5.14 that I fixed recently: tell($glob_copy) would, due to its implicit *{}, set PL_last_in_gv to a mortal copy of $glob_copy, which would be freed at the end of the statement, resulting in PL_last_in_gv simply not being set.  I fixed it by flagging the rv2gv so that it will return a fake glob if passed one.



Now we have an interesting discrepancy: tell(*$glob_copy) will set PL_last_in_gv, but tell(*{*$glob_copy}) won’t, because the inner * doesn’t get the flag set.  (For those who don’t know, *{...} returns its argument if it is a glob not flagged FAKE, so *{*{*{*{*foo}}}} is the same as *foo.)

Also, it used to be possible to write

   $fh = *STDOUT;
   $fh = 3;
   $fh = *STDOUT;

And have PL_last_in_gv still pointing to $fh (effectively to STDOUT).  Now it’s not.

We have three expectations that we are trying to meet, which are in conflict:

1. PL_last_in_gv remembers the last filehandle that was accessed.
2. Assigning to the last handle’s glob can change what handle it
  points to one (<STDOUT>; *STDOUT = *foo).
3. Filehandles are closed automatically on scope exit if they are only
  referenced in that scope.

Item number 1 can be solved by making PL_last_in_gv reference-counted.  But that will break number 3.

Item number 2 is probably impossible to solve in the presence of fake globs, except perhaps by discouraging their use.  Yet they actually provide a very useful feature:

   $fh = *STDOUT;
   $fh->autoflush(1); # ok
   $fh = \*STDOUT;
   $fh->autoflush(1); # Can't call a method on a unblessed reference


One possible solution:

To make PL_last_in_gv remember the last filehandle even when fake globs are involved, when a glob is freed or coerced, and that glob is in PL_last_in_gv, set PL_last_in_gv to a new glob with the same name and the same IO but with a weak reference in its IO slot.  PL_last_in_gv with hold a reference count on that glob.  Since there can only be one such glob, we need only one gv flag for that.  When the IO thing is freed and the backreference killed, we can free the glob in PL_last_in_gv and set the latter to null.

That would allow 1 to work, without breaking 3, but it’s a twisted maze of action at a distance (like the rest of perl, come to think of it).

As for number 2, it already works with normal globs, but not with fake globs.  I don’t think we can make it work.  It is currently the same with select().



This is a mess.

Site configuration information for perl 5.15.5:

Configured by sprout at Sun Dec 18 11:26:14 PST 2011.

Summary of my perl5 (revision 5 version 15 subversion 5) configuration:
 Snapshot of: 5dca8ed9d28127c9f7a2e7ce5f8ba970da3608cd
   osname=darwin, osvers=10.5.0, archname=darwin-2level
   uname='darwin pint.local 10.5.0 darwin kernel version 10.5.0: fri nov 5 23:20:39 pdt 2010; root:xnu-1504.9.17~1release_i386 i386 '
   config_args='-de -Dusedevel -DDEBUGGING=-g'
   hint=recommended, useposix=true, d_sigaction=define
   useithreads=undef, usemultiplicity=undef
   useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
   use64bitint=undef, use64bitall=undef, uselongdouble=undef
   usemymalloc=n, bincompat5005=undef
   cc='cc', ccflags ='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include',
   optimize='-O3 -g',
   cppflags='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
   ccversion='', gccversion='4.2.1 (Apple Inc. build 5664)', gccosandvers=''
   intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
   d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
   ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
   alignbytes=8, prototype=define
 Linker and Libraries:
   ld='env MACOSX_DEPLOYMENT_TARGET=10.3 cc', ldflags =' -fstack-protector -L/usr/local/lib'
   libpth=/usr/local/lib /usr/lib
   libs=-ldbm -ldl -lm -lutil -lc
   perllibs=-ldl -lm -lutil -lc
   libc=, so=dylib, useshrplib=false, libperl=libperl.a
 Dynamic Linking:
   dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
   cccdlflags=' ', lddlflags=' -bundle -undefined dynamic_lookup -L/usr/local/lib -fstack-protector'

Locally applied patches:

@INC for perl 5.15.5:

Environment for perl 5.15.5:
   LANGUAGE (unset)
   LD_LIBRARY_PATH (unset)
   LOGDIR (unset)
   PERL_BADLANG (unset)
   SHELL=/bin/bash Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About