Fix up .gitignore files some more
[p5sagit/p5-mst-13.2.git] / ext / threads / shared / shared.pm
index 4ab12db..1409a1c 100644 (file)
@@ -5,7 +5,9 @@ use 5.008;
 use strict;
 use warnings;
 
-our $VERSION = '1.03';
+use Scalar::Util qw(reftype refaddr blessed);
+
+our $VERSION = '1.27';
 my $XS_VERSION = $VERSION;
 $VERSION = eval $VERSION;
 
@@ -41,7 +43,7 @@ sub import
 {
     # Exported subroutines
     my @EXPORT = qw(share is_shared cond_wait cond_timedwait
-                    cond_signal cond_broadcast);
+                    cond_signal cond_broadcast shared_clone);
     if ($threads::threads) {
         push(@EXPORT, 'bless');
     }
@@ -55,6 +57,10 @@ sub import
 }
 
 
+# Predeclarations for internal functions
+my ($make_shared);
+
+
 ### Methods, etc. ###
 
 sub threads::shared::tie::SPLICE
@@ -63,6 +69,114 @@ sub threads::shared::tie::SPLICE
     Carp::croak('Splice not implemented for shared arrays');
 }
 
+
+# Create a thread-shared clone of a complex data structure or object
+sub shared_clone
+{
+    if (@_ != 1) {
+        require Carp;
+        Carp::croak('Usage: shared_clone(REF)');
+    }
+
+    return $make_shared->(shift, {});
+}
+
+
+### Internal Functions ###
+
+# Used by shared_clone() to recursively clone
+#   a complex data structure or object
+$make_shared = sub {
+    my ($item, $cloned) = @_;
+
+    # Just return the item if:
+    # 1. Not a ref;
+    # 2. Already shared; or
+    # 3. Not running 'threads'.
+    return $item if (! ref($item) || is_shared($item) || ! $threads::threads);
+
+    # Check for previously cloned references
+    #   (this takes care of circular refs as well)
+    my $addr = refaddr($item);
+    if (exists($cloned->{$addr})) {
+        # Return the already existing clone
+        return $cloned->{$addr};
+    }
+
+    # Make copies of array, hash and scalar refs and refs of refs
+    my $copy;
+    my $ref_type = reftype($item);
+
+    # Copy an array ref
+    if ($ref_type eq 'ARRAY') {
+        # Make empty shared array ref
+        $copy = &share([]);
+        # Add to clone checking hash
+        $cloned->{$addr} = $copy;
+        # Recursively copy and add contents
+        push(@$copy, map { $make_shared->($_, $cloned) } @$item);
+    }
+
+    # Copy a hash ref
+    elsif ($ref_type eq 'HASH') {
+        # Make empty shared hash ref
+        $copy = &share({});
+        # Add to clone checking hash
+        $cloned->{$addr} = $copy;
+        # Recursively copy and add contents
+        foreach my $key (keys(%{$item})) {
+            $copy->{$key} = $make_shared->($item->{$key}, $cloned);
+        }
+    }
+
+    # Copy a scalar ref
+    elsif ($ref_type eq 'SCALAR') {
+        $copy = \do{ my $scalar = $$item; };
+        share($copy);
+        # Add to clone checking hash
+        $cloned->{$addr} = $copy;
+    }
+
+    # Copy of a ref of a ref
+    elsif ($ref_type eq 'REF') {
+        # Special handling for $x = \$x
+        if ($addr == refaddr($$item)) {
+            $copy = \$copy;
+            share($copy);
+            $cloned->{$addr} = $copy;
+        } else {
+            my $tmp;
+            $copy = \$tmp;
+            share($copy);
+            # Add to clone checking hash
+            $cloned->{$addr} = $copy;
+            # Recursively copy and add contents
+            $tmp = $make_shared->($$item, $cloned);
+        }
+
+    } else {
+        require Carp;
+        Carp::croak("Unsupported ref type: ", $ref_type);
+    }
+
+    # If input item is an object, then bless the copy into the same class
+    if (my $class = blessed($item)) {
+        bless($copy, $class);
+    }
+
+    # Clone READONLY flag
+    if ($ref_type eq 'SCALAR') {
+        if (Internals::SvREADONLY($$item)) {
+            Internals::SvREADONLY($$copy, 1) if ($] >= 5.008003);
+        }
+    }
+    if (Internals::SvREADONLY($item)) {
+        Internals::SvREADONLY($copy, 1) if ($] >= 5.008003);
+    }
+
+    return $copy;
+};
+
 1;
 
 __END__
@@ -73,7 +187,7 @@ threads::shared - Perl extension for sharing data structures between threads
 
 =head1 VERSION
 
-This document describes threads::shared version 1.03
+This document describes threads::shared version 1.27
 
 =head1 SYNOPSIS
 
@@ -81,16 +195,28 @@ This document describes threads::shared version 1.03
   use threads::shared;
 
   my $var :shared;
-  $var = $scalar_value;
-  $var = $shared_ref_value;
-  $var = share($simple_unshared_ref_value);
+  my %hsh :shared;
+  my @ary :shared;
 
-  my($scalar, @array, %hash);
+  my ($scalar, @array, %hash);
   share($scalar);
   share(@array);
   share(%hash);
-  my $bar = &share([]);
-  $hash{bar} = &share({});
+
+  $var = $scalar_value;
+  $var = $shared_ref_value;
+  $var = shared_clone($non_shared_ref_value);
+  $var = shared_clone({'foo' => [qw/foo bar baz/]});
+
+  $hsh{'foo'} = $scalar_value;
+  $hsh{'bar'} = $shared_ref_value;
+  $hsh{'baz'} = shared_clone($non_shared_ref_value);
+  $hsh{'quz'} = shared_clone([1..3]);
+
+  $ary[0] = $scalar_value;
+  $ary[1] = $shared_ref_value;
+  $ary[2] = shared_clone($non_shared_ref_value);
+  $ary[3] = shared_clone([ {}, [] ]);
 
   { lock(%hash); ...  }
 
@@ -108,13 +234,17 @@ This document describes threads::shared version 1.03
 
 By default, variables are private to each thread, and each newly created
 thread gets a private copy of each existing variable.  This module allows you
-to share variables across different threads (and pseudoforks on Win32).  It is
-used together with the L<threads> module.
+to share variables across different threads (and pseudo-forks on Win32).  It
+is used together with the L<threads> module.
+
+This module supports the sharing of the following data types only:  scalars
+and scalar refs, arrays and array refs, and hashes and hash refs.
 
 =head1 EXPORT
 
-C<share>, C<cond_wait>, C<cond_timedwait>, C<cond_signal>, C<cond_broadcast>,
-C<is_shared>
+The following functions are exported by this module: C<share>,
+C<shared_clone>, C<is_shared>, C<cond_wait>, C<cond_timedwait>, C<cond_signal>
+and C<cond_broadcast>
 
 Note that if this module is imported when L<threads> has not yet been loaded,
 then these functions all become no-ops.  This makes it possible to write
@@ -126,33 +256,60 @@ modules that will work in both threaded and non-threaded environments.
 
 =item share VARIABLE
 
-C<share> takes a value and marks it as shared. You can share a scalar, array,
-hash, scalar ref, array ref, or hash ref.  C<share> will return the shared
-rvalue, but always as a reference.
+C<share> takes a variable and marks it as shared:
 
-A variable can also be marked as shared at compile time by using the
-C<:shared> attribute: C<my $var :shared;>.
+  my ($scalar, @array, %hash);
+  share($scalar);
+  share(@array);
+  share(%hash);
 
-Due to problems with Perl's prototyping, if you want to share a newly created
-reference, you need to use the C<&share([])> and C<&share({})> syntax.
+C<share> will return the shared rvalue, but always as a reference.
 
-The only values that can be assigned to a shared scalar are other scalar
-values, or shared refs:
+Variables can also be marked as shared at compile time by using the
+C<:shared> attribute:
 
-  my $var :shared;
-  $var = 1;              # ok
-  $var = [];             # error
-  $var = &share([]);     # ok
+  my ($var, %hash, @array) :shared;
+
+Shared variables can only store scalars, refs of shared variables, or
+refs of shared data (discussed in next section):
+
+  my ($var, %hash, @array) :shared;
+  my $bork;
+
+  # Storing scalars
+  $var = 1;
+  $hash{'foo'} = 'bar';
+  $array[0] = 1.5;
+
+  # Storing shared refs
+  $var = \%hash;
+  $hash{'ary'} = \@array;
+  $array[1] = \$var;
+
+  # The following are errors:
+  #   $var = \$bork;                    # ref of non-shared variable
+  #   $hash{'bork'} = [];               # non-shared array ref
+  #   push(@array, { 'x' => 1 });       # non-shared hash ref
 
-C<share> will traverse up references exactly I<one> level.  C<share(\$a)> is
-equivalent to C<share($a)>, while C<share(\\$a)> is not.  This means that you
-must create nested shared data structures by first creating individual shared
-leaf nodes, and then adding them to a shared hash or array.
+=item shared_clone REF
 
-  my %hash :shared;
-  $hash{'meaning'} = &share([]);
-  $hash{'meaning'}[0] = &share({});
-  $hash{'meaning'}[0]{'life'} = 42;
+C<shared_clone> takes a reference, and returns a shared version of its
+argument, performing a deep copy on any non-shared elements.  Any shared
+elements in the argument are used as is (i.e., they are not cloned).
+
+  my $cpy = shared_clone({'foo' => [qw/foo bar baz/]});
+
+Object status (i.e., the class an object is blessed into) is also cloned.
+
+  my $obj = {'foo' => [qw/foo bar baz/]};
+  bless($obj, 'Foo');
+  my $cpy = shared_clone($obj);
+  print(ref($cpy), "\n");         # Outputs 'Foo'
+
+For cloning empty array or hash refs, the following may also be used:
+
+  $var = &share([]);   # Same as $var = shared_clone([]);
+  $var = &share({});   # Same as $var = shared_clone({});
 
 =item is_shared VARIABLE
 
@@ -170,16 +327,16 @@ L<refaddr()|Scalar::Util/"refaddr EXPR">).  Otherwise, returns C<undef>.
 
 C<lock> places a lock on a variable until the lock goes out of scope.  If the
 variable is locked by another thread, the C<lock> call will block until it's
-available.  C<lock> is recursive, so multiple calls to C<lock> are safe -- the
-variable will remain locked until the outermost lock on the variable goes out
-of scope.
+available.  Multiple calls to C<lock> by the same thread from within
+dynamically nested scopes are safe -- the variable will remain locked until
+the outermost lock on the variable goes out of scope.
 
-If a container object, such as a hash or array, is locked, all the elements of
-that container are not locked.  For example, if a thread does a C<lock @a>,
-any other thread doing a C<lock($a[12])> won't block.
+Locking a container object, such as a hash or array, doesn't lock the elements
+of that container. For example, if a thread does a C<lock(@a)>, any other
+thread doing a C<lock($a[12])> won't block.
 
-C<lock> will traverse up references exactly I<one> level.  C<lock(\$a)> is
-equivalent to C<lock($a)>, while C<lock(\\$a)> is not.
+C<lock()> follows references exactly I<one> level.  C<lock(\$a)> is equivalent
+to C<lock($a)>, while C<lock(\\$a)> is not.
 
 Note that you cannot explicitly unlock a variable; you can only wait for the
 lock to go out of scope.  This is most easily accomplished by locking the
@@ -205,11 +362,11 @@ the variable, and blocks until another thread does a C<cond_signal> or
 C<cond_broadcast> for that same locked variable.  The variable that
 C<cond_wait> blocked on is relocked after the C<cond_wait> is satisfied.  If
 there are multiple threads C<cond_wait>ing on the same variable, all but one
-will reblock waiting to reacquire the lock on the variable. (So if you're only
+will re-block waiting to reacquire the lock on the variable. (So if you're only
 using C<cond_wait> for synchronisation, give up the lock as soon as possible).
 The two actions of unlocking the variable and entering the blocked wait state
 are atomic, the two actions of exiting from the blocked wait state and
-relocking the variable are not.
+re-locking the variable are not.
 
 In its second form, C<cond_wait> takes a shared, B<unlocked> variable followed
 by a shared, B<locked> variable.  The second variable is unlocked and thread
@@ -262,7 +419,7 @@ signaling before another thread has entered cond_wait().
 
 C<cond_signal> will normally generate a warning if you attempt to use it on an
 unlocked variable. On the rare occasions where doing this may be sensible, you
-can skip the warning with:
+can suppress the warning with:
 
   { no warnings 'threads'; cond_signal($foo); }
 
@@ -277,19 +434,15 @@ a C<cond_wait> on the locked variable, rather than only one.
 =head1 OBJECTS
 
 L<threads::shared> exports a version of L<bless()|perlfunc/"bless REF"> that
-works on shared objects such that i<blessings> propagate across threads.
+works on shared objects such that I<blessings> propagate across threads.
 
-  # Create a shared 'foo' object
-  my $foo;
-  share($foo);
-  $foo = &share({});
-  bless($foo, 'foo');
+  # Create a shared 'Foo' object
+  my $foo :shared = shared_clone({});
+  bless($foo, 'Foo');
 
-  # Create a shared 'bar' object
-  my $bar;
-  share($bar);
-  $bar = &share({});
-  bless($bar, 'bar');
+  # Create a shared 'Bar' object
+  my $bar :shared = shared_clone({});
+  bless($bar, 'Bar');
 
   # Put 'bar' inside 'foo'
   $foo->{'bar'} = $bar;
@@ -297,26 +450,29 @@ works on shared objects such that i<blessings> propagate across threads.
   # Rebless the objects via a thread
   threads->create(sub {
       # Rebless the outer object
-      bless($foo, 'yin');
+      bless($foo, 'Yin');
 
       # Cannot directly rebless the inner object
-      #bless($foo->{'bar'}, 'yang');
+      #bless($foo->{'bar'}, 'Yang');
 
       # Retrieve and rebless the inner object
       my $obj = $foo->{'bar'};
-      bless($obj, 'yang');
+      bless($obj, 'Yang');
       $foo->{'bar'} = $obj;
 
   })->join();
 
-  print(ref($foo),          "\n");    # Prints 'yin'
-  print(ref($foo->{'bar'}), "\n");    # Prints 'yang'
-  print(ref($bar),          "\n");    # Also prints 'yang'
+  print(ref($foo),          "\n");    # Prints 'Yin'
+  print(ref($foo->{'bar'}), "\n");    # Prints 'Yang'
+  print(ref($bar),          "\n");    # Also prints 'Yang'
 
 =head1 NOTES
 
-threads::shared is designed to disable itself silently if threads are not
-available. If you want access to threads, you must C<use threads> before you
+L<threads::shared> is designed to disable itself silently if threads are not
+available.  This allows you to write modules and packages that can be used
+in both threaded and non-threaded applications.
+
+If you want access to threads, you must C<use threads> before you
 C<use threads::shared>.  L<threads> will emit a warning if you use it after
 L<threads::shared>.
 
@@ -342,10 +498,11 @@ Therefore, populate such variables B<after> declaring them as shared.  (Scalar
 and scalar refs are not affected by this problem.)
 
 It is often not wise to share an object unless the class itself has been
-written to support sharing. For example, an object's destructor may get called
-multiple times, one for each thread's scope exit.  Another example, is that
-the contents of hash-based objects will be lost due to the above mentioned
-limitation.
+written to support sharing.  For example, an object's destructor may get
+called multiple times, once for each thread's scope exit.  Another danger is
+that the contents of hash-based objects will be lost due to the above
+mentioned limitation.  See F<examples/class.pl> (in the CPAN distribution of
+this module) for how to create a class that supports object sharing.
 
 Does not support C<splice> on arrays!
 
@@ -358,8 +515,47 @@ error message.  But the C<< $hashref->{key} >> is B<not> shared, causing the
 error "locking can only be used on shared values" to occur when you attempt to
 C<< lock($hasref->{key}) >>.
 
+Using L<refaddr()|Scalar::Util/"refaddr EXPR">) is unreliable for testing
+whether or not two shared references are equivalent (e.g., when testing for
+circular references).  Use L<is_shared()/"is_shared VARIABLE">, instead:
+
+    use threads;
+    use threads::shared;
+    use Scalar::Util qw(refaddr);
+
+    # If ref is shared, use threads::shared's internal ID.
+    # Otherwise, use refaddr().
+    my $addr1 = is_shared($ref1) || refaddr($ref1);
+    my $addr2 = is_shared($ref2) || refaddr($ref2);
+
+    if ($addr1 == $addr2) {
+        # The refs are equivalent
+    }
+
+L<each()|perlfunc/"each HASH"> does not work properly on shared references
+embedded in shared structures.  For example:
+
+    my %foo :shared;
+    $foo{'bar'} = shared_clone({'a'=>'x', 'b'=>'y', 'c'=>'z'});
+
+    while (my ($key, $val) = each(%{$foo{'bar'}})) {
+        ...
+    }
+
+Either of the following will work instead:
+
+    my $ref = $foo{'bar'};
+    while (my ($key, $val) = each(%{$ref})) {
+        ...
+    }
+
+    foreach my $key (keys(%{$foo{'bar'}})) {
+        my $val = $foo{'bar'}{$key};
+        ...
+    }
+
 View existing bug reports at, and submit any new bugs, problems, patches, etc.
-to: L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=threads-shared>
+to: L<http://rt.cpan.org/Public/Dist/Display.html?Name=threads-shared>
 
 =head1 SEE ALSO
 
@@ -367,7 +563,10 @@ L<threads::shared> Discussion Forum on CPAN:
 L<http://www.cpanforum.com/dist/threads-shared>
 
 Annotated POD for L<threads::shared>:
-L<http://annocpan.org/~JDHEDDEN/threads-shared-1.03/shared.pm>
+L<http://annocpan.org/~JDHEDDEN/threads-shared-1.27/shared.pm>
+
+Source repository:
+L<http://code.google.com/p/threads-shared/>
 
 L<threads>, L<perlthrtut>
 
@@ -381,10 +580,12 @@ L<http://lists.cpan.org/showlist.cgi?name=iThreads>
 
 Artur Bergman E<lt>sky AT crucially DOT netE<gt>
 
-threads::shared is released under the same license as Perl.
-
 Documentation borrowed from the old Thread.pm.
 
 CPAN version produced by Jerry D. Hedden E<lt>jdhedden AT cpan DOT orgE<gt>.
 
+=head1 LICENSE
+
+threads::shared is released under the same license as Perl.
+
 =cut