multithreadingperl

Is there a bug with shared hashes and localized hash elements?


I have some code that feels like a bug in Perl, but am I missing something? Basically, when I localize a hash element in a shared hash, the element still exists (with the undef value) after it leaves the scope.

For example:

use threads;
use threads::shared;

my $hash1 = {};
{ local $hash1->{'desc'} = "blah" }
print exists($hash1->{'desc'}) ? "Hash: exists\n" : "Hash: does not exist\n";

my $hash2 = shared_clone({});
{ local $hash2->{'desc'} = "blah" }
print exists($hash2->{'desc'}) ? "Shared hash: exists\n" : "Shared hash: does not exist\n";
print "Shared hash: is undef\n" if !defined($hash2->{'desc'});

Which prints the following for perl v5.34.0:

Hash: does not exist
Shared hash: exists
Shared hash: is undef

I found a very similar bug that was apparently fixed in perl v5.8.0 for tied hashes. I'm wondering if shared hashes are something different than "tied" and therefore still have the bug?

The bug identified in perldoc perl58delta as being fixed:

  • Localised hash elements (and %ENV) are correctly unlocalised to not exist, if they didn't before they were localised.

    use Tie::Hash;
    tie my %tied_hash => 'Tie::StdHash';
    
    ...
    
    # Nothing has set the FOO element so far
    
    { local $tied_hash{FOO} = 'Bar' }
    
    # This used to print, but not now.
    print "exists!\n" if exists $tied_hash{FOO};
    

    As a side effect of this fix, tied hash interfaces must define the EXISTS and DELETE methods.


Solution

  • You should get the same behaviour from both hashes, so this is a bug. I have filed a bug report.


    Cleaned up program:

    use strict;
    use warnings;
    
    use threads;
    use threads::shared;
    
    sub test {
       my $name = shift;
       my $h    = shift;
    
       { local $h->{ x } = "blah"; }
    
       printf( "%s: %s\n",
          $name,
          ( !exists(  $h->{ x } ) ? "!exists"
          : !defined( $h->{ x } ) ? "exists && !defined"
          :                         "exists && defined"
          )
       );
    }
    
    { my %h;         test( "Normal hash", \%h ); }
    { my %h :shared; test( "Shared hash", \%h ); }
    
    $ perl -v | grep 'This is'
    This is perl 5, version 38, subversion 0 (v5.38.0) built for x86_64-linux-thread-multi
    
    $ perl a.pl
    Normal hash: !exists
    Shared hash: exists && !defined