Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[perl #28233] Reference to tied hash element caches value too aggressively

2 views
Skip to first unread message

Jamie Lokier

unread,
Apr 4, 2004, 7:13:05 PM4/4/04
to bugs-bi...@netlabs.develooper.com
# New Ticket Created by Jamie Lokier
# Please include the string: [perl #28233]
# in the subject line of all future correspondence about this issue.
# <URL: http://rt.perl.org:80/rt3/Ticket/Display.html?id=28233 >

This is a bug report for perl from ja...@shareable.org,
generated with the help of perlbug 1.34 running under perl v5.8.0.


-----------------------------------------------------------------
[Please enter your report here]

It is possible to take a reference to an element of a tied hash, like this:

my $ref = \$tied_hash{key};

Taking the reference like that creates a special kind of
pseudo-reference which remembers the key that was used. It doesn't
call FETCH or STORE or any other method.

Reading from the reference (C<my $value = $$ref>) calls the tied
hash's FETCH method the _first_ time it's read, and then caches the
result.

Writing to the reference (C<$$ref = ...>) calls the tied hash's STORE
method and invalidates the cached result, so that FETCH will be called
next time the reference is read.

This is very clever and sensible and useful.

Confusion comes about when the hash is written to directly, or via
another pseudo-reference using the same key.

This code prints "new_value":

$tied_hash{key} = "old_value";
my $ref = \$tied_hash{key};
$tied_hash{key} = "new_value";
print $$ref;

Whereas this code prints "old_value" _if_ C<$tied_hash> is tied:

$tied_hash{key} = "old_value";
my $ref = \$tied_hash{key};
"$$ref"; # Force non-void context.
$tied_hash{key} = "new_value";
print $$ref;

This also prints "old_value" if C<$tied_hash> is tied:

$tied_hash{key} = "old_value";
my $ref1 = \$tied_hash{key};
my $ref2 = \$tied_hash{key};
"$$ref1"; # Force non-void context.
$$ref2 = "new_value";
print $$ref1;

My complaint is that this cacheing of fetched values in a
pseudo-reference is slightly too aggressive: it makes references to
tied hash elements behave semantically too differently from ordinary
hashes, and it isn't necessary or difficult to fix.

Ideally, any write to that key of the tied hash should invalidate the
cached pseudo-reference value. (Really ideally, all pseudo-references
using the same key would be equal , too).

It may well be to much too state to keep track of, remembering the
pseudo-references of each key individually. So instead, I suggest any
write to a tied hash should invalidate _all_ cached pseudo-references
which are associated with that hash. That wouldn't be much state to
keep track of.

Example program:

perl -l <<'END'
sub TIEHASH { return bless {}, $_[0] }
sub FETCH { print "Fetch"; return $_[0]->{$_[1]} }
sub STORE { print "Store"; $_[0]->{$_[1]} = $_[2] }
tie %HASH, __PACKAGE__; my $r = \$HASH{key};
$$r = "value1";
print $$r;
print $HASH{key};
print $$r;
$$r = "value2";
print $$r;
print $$r;
$HASH{key} = "value3";
print $$r;
print $HASH{key};
print $$r;
END

Output of the above program:

Store
Fetch
value1
Fetch
value1
value1
Store
Fetch
value2
value2
Store
value2
Fetch
value3
value2

See how the "value2" lines after the last "Store" should logically say
"value3". I think this quirk of pseudo-references is too subtle, and
is likely to cause subtle bugs in programs, especially code that is
given a tied hash and doesn't know it, treating it like a normal hash.

Thanks,
-- Jamie

[Please do not change anything below this line]
-----------------------------------------------------------------
---
Flags:
category=core
severity=medium
---
Site configuration information for perl v5.8.0:

Configured by bhcompile'
cf_email='bhcompile at Wed Aug 13 11:45:59 EDT 2003.

Summary of my rderl (revision 5.0 version 8 subversion 0) configuration:
Platform:
osname=linux, osvers=2.4.21-1.1931.2.382.entsmp, archname=i386-linux-thread-multi
uname='linux str'
config_args='-des -Doptimize=-O2 -g -pipe -march=i386 -mcpu=i686 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dinstallprefix=/usr -Dprefix=/usr -Darchname=i386-linux -Dvendorprefix=/usr -Dsiteprefix=/usr -Dotherlibdirs=/usr/lib/perl5/5.8.0 -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr'
hint=recommended, useposix=true, d_sigaction=define
usethreads=define use5005threads=undef'
useithreads=define usemultiplicity=
useperlio= d_sfio=undef uselargefiles=define usesocks=undef
use64bitint=undef use64bitall=un uselongdouble=
usemymalloc=, bincompat5005=undef
Compiler:
cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBUGGING -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
optimize='',
cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBUGGING -fno-strict-aliasing -I/usr/local/include -I/usr/include/gdbm'
ccversion='', gccversion='3.2.2 20030222 (Red Hat Linux 3.2.2-5)', gccosandvers=''
gccversion='3.2.2 200302'
intsize=r, longsize=r, ptrsize=5, doublesize=8, byteorder=1234
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
ivtype='long'
k', ivsize=4'
ivtype='l, nvtype='double'
o_nonbl', nvsize=, Off_t='', lseeksize=8
alignbytes=4, prototype=define
Linker and Libraries:
ld='gcc'
l', ldflags =' -L/u'
libpth=/usr/local/lib /lib /usr/lib
libs=-lnsl -lgdbm -ldb -ldl -lm -lpthread -lc -lcrypt -lutil
perllibs=
libc=/lib/libc-2.3.2.so, so=so, useshrplib=true, libperl=libper
gnulibc_version='2.3.2'
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=so', d_dlsymun=undef, ccdlflags='-rdynamic -Wl,-rpath,/usr/lib/perl5/5.8.0/i386-linux-thread-multi/CORE'
cccdlflags='-fPIC'
ccdlflags='-rdynamic -Wl,-rpath,/usr/lib/perl5', lddlflags='s Unicode/Normalize XS/A'

Locally applied patches:
MAINT18379

---
@INC for perl v5.8.0:
/usr/lib/perl5/5.8.0/i386-linux-thread-multi
/usr/lib/perl5/5.8.0
/usr/lib/perl5/site_perl/5.8.0/i386-linux-thread-multi
/usr/lib/perl5/site_perl/5.8.0
/usr/lib/perl5/site_perl
/usr/lib/perl5/vendor_perl/5.8.0/i386-linux-thread-multi
/usr/lib/perl5/vendor_perl/5.8.0
/usr/lib/perl5/vendor_perl
/usr/lib/perl5/5.8.0/i386-linux-thread-multi
/usr/lib/perl5/5.8.0
.

---
Environment for perl v5.8.0:
HOME=/home/jamie
LANG=en_GB.UTF-8
LANGUAGE (unset)
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/jamie/bin
PERL_BADLANG (unset)
SHELL=/bin/bash
dlflags='-share (unset)

0 new messages