duelafn has asked for the wisdom of the Perl Monks concerning the following question:

I was trying to do some threaded computations and ran into a problem (keys in my shared hash become mysteriously undefined) when one of the subroutines that I called localized $_. If I change the subroutine to use a my variable, everything works fine. I was wondering whether this sort of behavior should have been expected or not.

Here is the simplified code that I'm using. The only interesting thing that happens is in the second to last line of code where I set $$x{foo}. If I use doit_local then $$x{path} becomes an undefined value, however if I use doit_my, $$x{path} retains its proper value.

#!/usr/bin/perl use 5.008; use strict; use warnings; use threads; use threads::shared; use Data::Dumper; my %p1 :shared; my %p2 :shared; %p1 = (path => shift); %p2 = (path => shift); my @t; push @t, threads->create(\&get_md5sums, \%p1); push @t, threads->create(\&get_md5sums, \%p2); $t[0]->join; $t[1]->join; print Dumper \%p1, \%p2; sub doit_local { local $_ = shift; s/'/'\''/g; return $_; } sub doit_my { my $x = shift; $x =~ s/'/'\''/g; return $x; } sub get_md5sums { my $x = shift; print Dumper [$x, $$x{path}]; $$x{foo} = join " ", map '"'.doit_local($_).'"', 'find', $$x{path}, +qw/-type f -exec md5sum {} ;/; return 1; }

Here is the output when I use doit_my. This is the desired output

[duelafn@jhegaala tmp]$ ./local_threads foo bar $VAR1 = [ { 'path' => 'foo' }, 'foo' ]; $VAR1 = [ { 'path' => 'bar' }, 'bar' ]; $VAR1 = { 'path' => 'foo', 'foo' => '"find" "foo" "-type" "f" "-exec" "md5sum" "{}" ";" +' }; $VAR2 = { 'path' => 'bar', 'foo' => '"find" "bar" "-type" "f" "-exec" "md5sum" "{}" ";" +' };

And this is what I get when I use doit_local. Before localizing $_ the shared hash has its correct values, however afterward, the path key is undefined.

[duelafn@jhegaala tmp]$ ./local_threads foo bar $VAR1 = [ { 'path' => 'foo' }, 'foo' ]; Use of uninitialized value in substitution (s///) at ./local_threads l +ine 25. Use of uninitialized value in concatenation (.) or string at ./local_t +hreads line 38. $VAR1 = [ { 'path' => 'bar' }, 'bar' ]; Use of uninitialized value in substitution (s///) at ./local_threads l +ine 25. Use of uninitialized value in concatenation (.) or string at ./local_t +hreads line 38. $VAR1 = { 'path' => undef, 'foo' => '"find" "" "-type" "f" "-exec" "md5sum" "{}" ";"' }; $VAR2 = { 'path' => undef, 'foo' => '"find" "" "-type" "f" "-exec" "md5sum" "{}" ";"' };

My perl version:

[duelafn@jhegaala tmp]$ perl --version This is perl, v5.8.4 built for i386-linux-thread-multi

Good Day,
    Dean

Replies are listed 'Best First'.
Re: Problem localizing $_ using threads::shared
by dave_the_m (Monsignor) on Nov 13, 2005 at 01:12 UTC
    Yes, you've just discovered the fact local $_ is broken in lots of interesting ways, mostly associated with internal magic. I've been doing some fixes in that area recently, so if you feel like reporting it using perlbug, I might get round to having a proper look at it sometime (there are already some existing local/shared bugs on my list to sort out at some point).

    Dave.

      Ah. Upon further study, there's nothing special about $_, it seems that any global or package variable that is bound to the original variable causes this.

      ## This code block causes problems use vars qw/ $g /; # or, "our $g;" sub doit_local { local $g = shift; $g =~ s/'/'\''/g; return $g; } sub get_md5sums { my $x = shift; print Dumper [$x, $$x{path}]; for $g ('find', $$x{path}, qw/-type f -exec md5sum {} ;/) { $$x{foo} .= '"'.doit_local($g).'"'; } return 1; }

      However, it is not a problem if we make a copy first

      ## This code block works as expected use vars qw/ $g /; # or, "our $g;" sub doit_local { local $g = shift; $g =~ s/'/'\''/g; return $g; } sub get_md5sums { my $x = shift; print Dumper [$x, $$x{path}]; for ('find', $$x{path}, qw/-type f -exec md5sum {} ;/) { $g = $_; $$x{foo} .= '"'.doit_local($g).'"'; } return 1; }

      I will write up some tests and report this using perlbug. Thanks a lot!

      Good Day,
          Dean

Re: Problem localizing $_ using threads::shared
by BrowserUk (Patriarch) on Nov 12, 2005 at 22:48 UTC

    I get the same results with either sub, but you don't show the code where you are calling doit_my().

    Is it possible that you made a mistake? In the code where you call the local version you have:

    $$x{foo} = join " ", map '"'.doit_local($_).'"', .....

    Ie. You are passing $_ into doit_local(), but nowhere in your code is $_ ever assigned a value?

    Inside the sub, you then local $_ and assign $_ to it, but you have still never assigned any value, so it is undef, whihch explains the 'uninitialized value' errors.

    If I call doit_my($_), I get exactly the same results and errors. What did you pass to doit_my()?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      I do have a map in there that sets $_. The doit_my version is exactly the same with s/doit_local/doit_my/ on that one line:

      $$x{foo} = join " ", map '"'.doit_my($_).'"', 'find', ...

      Good Day,
          Dean