Change 33931 by [EMAIL PROTECTED] on 2008/05/25 22:45:48
Integrate:
[ 33882]
Integrate:
[ 33809]
Subject: [PATCH - revised^2] threads::shared 1.19
From: "Jerry D. Hedden" <[EMAIL PROTECTED]>
Date: Wed, 7 May 2008 16:36:28 -0400
Message-ID: <[EMAIL PROTECTED]>
[ 33811]
Remove C++ comments
[ 33836]
Subject: [PATCH - revised] threads::shared 1.21
From: "Jerry D. Hedden" <[EMAIL PROTECTED]>
Date: Fri, 16 May 2008 09:52:24 -0400
Message-ID: <[EMAIL PROTECTED]>
[ 33883]
Integrate:
[ 33808]
Subject: [PATCH - revised] Thread::Queue 2.07
From: "Jerry D. Hedden" <[EMAIL PROTECTED]>
Date: Thu, 8 May 2008 10:05:51 -0400
Message-ID: <[EMAIL PROTECTED]>
[ 33847]
Subject: [PATCH] Thread::Queue 2.08
From: "Jerry D. Hedden" <[EMAIL PROTECTED]>
Date: Wed, 14 May 2008 12:47:04 -0400
Message-ID: <[EMAIL PROTECTED]>
[ 33871]
Subject: [PATCH] Thread::Semaphore 2.08
From: "Jerry D. Hedden" <[EMAIL PROTECTED]>
Date: Mon, 19 May 2008 13:06:20 -0400
Message-ID: <[EMAIL PROTECTED]>
Affected files ...
... //depot/maint-5.8/perl/MANIFEST#432 integrate
... //depot/maint-5.8/perl/ext/threads/shared/Makefile.PL#4 integrate
... //depot/maint-5.8/perl/ext/threads/shared/shared.pm#18 integrate
... //depot/maint-5.8/perl/ext/threads/shared/shared.xs#32 integrate
... //depot/maint-5.8/perl/ext/threads/shared/t/clone.t#1 branch
... //depot/maint-5.8/perl/ext/threads/shared/t/stress.t#5 integrate
... //depot/maint-5.8/perl/ext/threads/shared/t/sv_refs.t#4 integrate
... //depot/maint-5.8/perl/lib/Thread/Queue.pm#3 integrate
... //depot/maint-5.8/perl/lib/Thread/Queue/t/02_refs.t#2 integrate
... //depot/maint-5.8/perl/lib/Thread/Semaphore.pm#5 integrate
Differences ...
==== //depot/maint-5.8/perl/MANIFEST#432 (text) ====
Index: perl/MANIFEST
--- perl/MANIFEST#431~33930~ 2008-05-25 15:33:32.000000000 -0700
+++ perl/MANIFEST 2008-05-25 15:45:48.000000000 -0700
@@ -964,6 +964,7 @@
ext/threads/shared/t/av_refs.t Tests for arrays containing references
ext/threads/shared/t/av_simple.t Tests for basic shared array
functionality.
ext/threads/shared/t/blessed.t Test blessed shared variables
+ext/threads/shared/t/clone.t Test shared cloning
ext/threads/shared/t/cond.t Test condition variables
ext/threads/shared/t/disabled.t Test threads::shared when threads are
disabled.
ext/threads/shared/t/hv_refs.t Test shared hashes containing references
==== //depot/maint-5.8/perl/ext/threads/shared/Makefile.PL#4 (xtext) ====
Index: perl/ext/threads/shared/Makefile.PL
--- perl/ext/threads/shared/Makefile.PL#3~33522~ 2008-03-13
17:11:24.000000000 -0700
+++ perl/ext/threads/shared/Makefile.PL 2008-05-25 15:45:48.000000000 -0700
@@ -65,6 +65,7 @@
'Config' => 0,
'Carp' => 0,
'XSLoader' => 0,
+ 'Scalar::Util' => 0,
'Test' => 0,
'Test::More' => 0,
==== //depot/maint-5.8/perl/ext/threads/shared/shared.pm#18 (text) ====
Index: perl/ext/threads/shared/shared.pm
--- perl/ext/threads/shared/shared.pm#17~33522~ 2008-03-13 17:11:24.000000000
-0700
+++ perl/ext/threads/shared/shared.pm 2008-05-25 15:45:48.000000000 -0700
@@ -5,7 +5,9 @@
use strict;
use warnings;
-our $VERSION = '1.18';
+use Scalar::Util qw(reftype refaddr blessed);
+
+our $VERSION = '1.21';
my $XS_VERSION = $VERSION;
$VERSION = eval $VERSION;
@@ -41,7 +43,7 @@
{
# 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 @@
}
+# Predeclarations for internal functions
+my ($make_shared);
+
+
### Methods, etc. ###
sub threads::shared::tie::SPLICE
@@ -63,6 +69,113 @@
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);
+ # Clone READONLY flag
+ if (Internals::SvREADONLY($$item)) {
+ Internals::SvREADONLY($$copy, 1);
+ }
+ # 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 (Internals::SvREADONLY($item)) {
+ Internals::SvREADONLY($copy, 1);
+ }
+
+ return $copy;
+};
+
1;
__END__
@@ -73,7 +186,7 @@
=head1 VERSION
-This document describes threads::shared version 1.18
+This document describes threads::shared version 1.21
=head1 SYNOPSIS
@@ -81,16 +194,28 @@
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);
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 +233,17 @@
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 pseudo-forks 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 +255,60 @@
=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:
+
+ my ($scalar, @array, %hash);
+ share($scalar);
+ share(@array);
+ share(%hash);
+
+C<share> will return the shared rvalue, but always as a reference.
-A variable can also be marked as shared at compile time by using the
-C<:shared> attribute: C<my $var :shared;>.
+Variables can also be marked as shared at compile time by using the
+C<:shared> attribute:
-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.
+ my ($var, %hash, @array) :shared;
-The only values that can be assigned to a shared scalar are other scalar
-values, or shared refs:
+Shared variables can only store scalars, refs of shared variables, or
+refs of shared data (discussed in next section):
- my $var :shared;
- $var = 1; # ok
- $var = []; # error
- $var = &share([]); # ok
-
-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.
-
- my %hash :shared;
- $hash{'meaning'} = &share([]);
- $hash{'meaning'}[0] = &share({});
- $hash{'meaning'}[0]{'life'} = 42;
+ 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'} = [EMAIL PROTECTED];
+ $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
+
+=item shared_clone REF
+
+C<shared_clone> takes a reference, and returns a shared version of its
+argument, preforming 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 = share_clone([]);
+ $var = &share({}); # Same as $var = share_clone({});
=item is_shared VARIABLE
@@ -279,17 +435,13 @@
L<threads::shared> exports a version of L<bless()|perlfunc/"bless REF"> that
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 'bar' object
- my $bar;
- share($bar);
- $bar = &share({});
- bless($bar, 'bar');
+ # Create a shared 'Foo' object
+ my $foo :shared = shared_clone({});
+ bless($foo, 'Foo');
+
+ # Create a shared 'Bar' object
+ my $bar :shared = shared_clone({});
+ bless($bar, 'Bar');
# Put 'bar' inside 'foo'
$foo->{'bar'} = $bar;
@@ -297,21 +449,21 @@
# 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
@@ -362,6 +514,23 @@
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
+ }
+
View existing bug reports at, and submit any new bugs, problems, patches, etc.
to: L<http://rt.cpan.org/Public/Dist/Display.html?Name=threads-shared>
@@ -371,7 +540,7 @@
L<http://www.cpanforum.com/dist/threads-shared>
Annotated POD for L<threads::shared>:
-L<http://annocpan.org/~JDHEDDEN/threads-shared-1.18/shared.pm>
+L<http://annocpan.org/~JDHEDDEN/threads-shared-1.21/shared.pm>
Source repository:
L<http://code.google.com/p/threads-shared/>
==== //depot/maint-5.8/perl/ext/threads/shared/shared.xs#32 (text) ====
Index: perl/ext/threads/shared/shared.xs
--- perl/ext/threads/shared/shared.xs#31~33522~ 2008-03-13 17:11:24.000000000
-0700
+++ perl/ext/threads/shared/shared.xs 2008-05-25 15:45:48.000000000 -0700
@@ -712,6 +712,11 @@
ENTER_LOCK;
if (SvROK(ssv)) {
S_get_RV(aTHX_ sv, ssv);
+ /* Look ahead for refs of refs */
+ if (SvROK(SvRV(ssv))) {
+ SvROK_on(SvRV(sv));
+ S_get_RV(aTHX_ SvRV(sv), SvRV(ssv));
+ }
} else {
sv_setsv_nomg(sv, ssv);
}
@@ -880,9 +885,13 @@
/* Exists in the array */
if (SvROK(*svp)) {
S_get_RV(aTHX_ sv, *svp);
+ /* Look ahead for refs of refs */
+ if (SvROK(SvRV(*svp))) {
+ SvROK_on(SvRV(sv));
+ S_get_RV(aTHX_ SvRV(sv), SvRV(*svp));
+ }
} else {
- /* XXX Can this branch ever happen? DAPM */
- /* XXX assert("no such branch"); */
+ /* $ary->[elem] or $ary->{elem} is a scalar */
Perl_sharedsv_associate(aTHX_ sv, *svp);
sv_setsv(sv, *svp);
}
@@ -1336,6 +1345,8 @@
SV *ssv;
CODE:
myref = SvRV(myref);
+ if (SvMAGICAL(myref))
+ mg_get(myref);
if (SvROK(myref))
myref = SvRV(myref);
ssv = Perl_sharedsv_find(aTHX_ myref);
==== //depot/maint-5.8/perl/ext/threads/shared/t/clone.t#1 (text) ====
Index: perl/ext/threads/shared/t/clone.t
--- /dev/null 2008-05-07 15:08:24.549929899 -0700
+++ perl/ext/threads/shared/t/clone.t 2008-05-25 15:45:48.000000000 -0700
@@ -0,0 +1,159 @@
+use strict;
+use warnings;
+
+BEGIN {
+ if ($ENV{'PERL_CORE'}){
+ chdir 't';
+ unshift @INC, '../lib';
+ }
+ use Config;
+ if (! $Config{'useithreads'}) {
+ print("1..0 # Skip: Perl not compiled with 'useithreads'\n");
+ exit(0);
+ }
+}
+
+use ExtUtils::testlib;
+
+sub ok {
+ my ($id, $ok, $name) = @_;
+
+ # You have to do it this way or VMS will get confused.
+ if ($ok) {
+ print("ok $id - $name\n");
+ } else {
+ print("not ok $id - $name\n");
+ printf("# Failed test at line %d\n", (caller)[2]);
+ }
+
+ return ($ok);
+}
+
+BEGIN {
+ $| = 1;
+ print("1..28\n"); ### Number of tests that will be run ###
+};
+
+my $test = 1;
+
+use threads;
+use threads::shared;
+ok($test++, 1, 'Loaded');
+
+### Start of Testing ###
+
+{
+ # Scalar
+ my $x = shared_clone(14);
+ ok($test++, $x == 14, 'number');
+
+ $x = shared_clone('test');
+ ok($test++, $x eq 'test', 'string');
+}
+
+{
+ my %hsh = ('foo' => 2);
+ eval {
+ my $x = shared_clone(%hsh);
+ };
+ ok($test++, $@ =~ /Usage:/, '1 arg');
+
+ threads->create(sub {})->join(); # Hide leaks, etc.
+}
+
+{
+ my $x = 'test';
+ my $foo :shared = shared_clone($x);
+ ok($test++, $foo eq 'test', 'cloned string');
+
+ $foo = shared_clone(\$x);
+ ok($test++, $$foo eq 'test', 'cloned scalar ref');
+
+ threads->create(sub {
+ ok($test++, $$foo eq 'test', 'cloned scalar ref in thread');
+ })->join();
+
+ $test++;
+}
+
+{
+ my $foo :shared;
+ $foo = shared_clone(\$foo);
+ ok($test++, ref($foo) eq 'REF', 'Circular ref typ');
+ ok($test++, threads::shared::_id($foo) == threads::shared::_id($$foo),
'Circular ref');
+
+ threads->create(sub {
+ ok($test++, threads::shared::_id($foo) == threads::shared::_id($$foo),
'Circular ref in thread');
+
+ my ($x, $y, $z);
+ $x = \$y; $y = \$z; $z = \$x;
+ $foo = shared_clone($x);
+ })->join();
+
+ $test++;
+
+ ok($test++, threads::shared::_id($$foo) == threads::shared::_id($$$$$foo),
+ 'Cloned circular refs from thread');
+}
+
+{
+ my @ary = (qw/foo bar baz/);
+ my $ary = shared_clone([EMAIL PROTECTED]);
+
+ ok($test++, $ary->[1] eq 'bar', 'Cloned array');
+ $ary->[1] = 99;
+ ok($test++, $ary->[1] == 99, 'Clone mod');
+ ok($test++, $ary[1] eq 'bar', 'Original array');
+
+ threads->create(sub {
+ ok($test++, $ary->[1] == 99, 'Clone mod in thread');
+
+ $ary[1] = 'bork';
+ $ary->[1] = 'thread';
+ })->join();
+
+ $test++;
+
+ ok($test++, $ary->[1] eq 'thread', 'Clone mod from thread');
+ ok($test++, $ary[1] eq 'bar', 'Original array');
+}
+
+{
+ my $scalar = 'zip';
+
+ my $obj = {
+ 'ary' => [ 1, 'foo', [ 86 ], { 'bar' => [ 'baz' ] } ],
+ 'ref' => \$scalar,
+ };
+
+ $obj->{'self'} = $obj;
+
+ bless($obj, 'Foo');
+
+ my $copy :shared;
+
+ threads->create(sub {
+ $copy = shared_clone($obj);
+
+ ok($test++, ${$copy->{'ref'}} eq 'zip', 'Obj ref in thread');
+ ok($test++, threads::shared::_id($copy) ==
threads::shared::_id($copy->{'self'}), 'Circular ref in cloned obj');
+ ok($test++, is_shared($copy->{'ary'}->[2]), 'Shared element in cloned
obj');
+ })->join();
+
+ $test += 3;
+
+ ok($test++, ref($copy) eq 'Foo', 'Obj cloned by thread');
+ ok($test++, ${$copy->{'ref'}} eq 'zip', 'Obj ref in thread');
+ ok($test++, threads::shared::_id($copy) ==
threads::shared::_id($copy->{'self'}), 'Circular ref in cloned obj');
+ ok($test++, $copy->{'ary'}->[3]->{'bar'}->[0] eq 'baz', 'Deeply cloned');
+ ok($test++, ref($copy) eq 'Foo', 'Cloned object class');
+}
+
+{
+ my $hsh :shared = shared_clone({'foo' => [qw/foo bar baz/]});
+ ok($test++, is_shared($hsh), 'Shared hash ref');
+ ok($test++, is_shared($hsh->{'foo'}), 'Shared hash ref elem');
+ ok($test++, $$hsh{'foo'}[1] eq 'bar', 'Cloned structure');
+}
+
+# EOF
==== //depot/maint-5.8/perl/ext/threads/shared/t/stress.t#5 (text) ====
Index: perl/ext/threads/shared/t/stress.t
--- perl/ext/threads/shared/t/stress.t#4~33522~ 2008-03-13 17:11:24.000000000
-0700
+++ perl/ext/threads/shared/t/stress.t 2008-05-25 15:45:48.000000000 -0700
@@ -79,25 +79,34 @@
# Gather thread results
my ($okay, $failures, $timeouts, $unknown) = (0, 0, 0, 0);
for (1..$cnt) {
- my $rc = $threads[$_]->join();
- if (! $rc) {
+ if (! $threads[$_]) {
$failures++;
- } elsif ($rc =~ /^timed out/) {
- $timeouts++;
- } elsif ($rc eq 'okay') {
- $okay++;
} else {
- $unknown++;
- print(STDERR "# Unknown error: $rc\n");
+ my $rc = $threads[$_]->join();
+ if (! $rc) {
+ $failures++;
+ } elsif ($rc =~ /^timed out/) {
+ $timeouts++;
+ } elsif ($rc eq 'okay') {
+ $okay++;
+ } else {
+ $unknown++;
+ print(STDERR "# Unknown error: $rc\n");
+ }
}
}
+ if ($failures) {
+ # Most likely due to running out of memory
+ print(STDERR "# Warning: $failures threads failed\n");
+ print(STDERR "# Note: errno 12 = ENOMEM\n");
+ $cnt -= $failures;
+ }
- if ($failures || $unknown || (($okay + $timeouts) != $cnt)) {
+ if ($unknown || (($okay + $timeouts) != $cnt)) {
print("not ok 1\n");
- my $too_few = $cnt - ($okay + $failures + $timeouts + $unknown);
+ my $too_few = $cnt - ($okay + $timeouts + $unknown);
print(STDERR "# Test failed:\n");
print(STDERR "#\t$too_few too few threads reported\n") if $too_few;
- print(STDERR "#\t$failures threads failed\n") if $failures;
print(STDERR "#\t$unknown unknown errors\n") if $unknown;
print(STDERR "#\t$timeouts threads timed out\n") if $timeouts;
==== //depot/maint-5.8/perl/ext/threads/shared/t/sv_refs.t#4 (text) ====
Index: perl/ext/threads/shared/t/sv_refs.t
--- perl/ext/threads/shared/t/sv_refs.t#3~30316~ 2007-02-15
05:28:31.000000000 -0800
+++ perl/ext/threads/shared/t/sv_refs.t 2008-05-25 15:45:48.000000000 -0700
@@ -31,7 +31,7 @@
BEGIN {
$| = 1;
- print("1..11\n"); ### Number of tests that will be run ###
+ print("1..21\n"); ### Number of tests that will be run ###
};
use threads;
@@ -74,4 +74,30 @@
ok(11, is_shared($foo), "Check for sharing");
+{
+ # Circular references with 3 shared scalars
+ my $x : shared;
+ my $y : shared;
+ my $z : shared;
+
+ $x = \$y;
+ $y = \$z;
+ $z = \$x;
+ ok(12, ref($x) eq 'REF', '$x ref type');
+ ok(13, ref($y) eq 'REF', '$y ref type');
+ ok(14, ref($z) eq 'REF', '$z ref type');
+
+ my @q :shared = ($x);
+ ok(15, ref($q[0]) eq 'REF', '$q[0] ref type');
+
+ my $w = $q[0];
+ ok(16, ref($w) eq 'REF', '$w ref type');
+ ok(17, ref($$w) eq 'REF', '$$w ref type');
+ ok(18, ref($$$w) eq 'REF', '$$$w ref type');
+ ok(19, ref($$$$w) eq 'REF', '$$$$w ref type');
+
+ ok(20, is_shared($x) == is_shared($w), '_id($x) == _id($w)');
+ ok(21, is_shared($w) == is_shared($$$$w), '_id($w) == _id($$$$w)');
+}
+
# EOF
==== //depot/maint-5.8/perl/lib/Thread/Queue.pm#3 (text) ====
Index: perl/lib/Thread/Queue.pm
--- perl/lib/Thread/Queue.pm#2~33516~ 2008-03-13 12:37:42.000000000 -0700
+++ perl/lib/Thread/Queue.pm 2008-05-25 15:45:48.000000000 -0700
@@ -3,19 +3,22 @@
use strict;
use warnings;
-our $VERSION = '2.06';
+our $VERSION = '2.08';
-use threads::shared 0.96;
-use Scalar::Util 1.10 qw(looks_like_number);
+use threads::shared 1.21;
+use Scalar::Util 1.10 qw(looks_like_number blessed reftype refaddr);
+
+# Carp errors from threads::shared calls should complain about caller
+our @CARP_NOT = ("threads::shared");
# Predeclarations for internal functions
-my ($make_shared, $validate_count, $validate_index);
+my ($validate_count, $validate_index);
# Create a new queue possibly pre-populated with items
sub new
{
my $class = shift;
- my @queue :shared = map { $make_shared->($_) } @_;
+ my @queue :shared = map { shared_clone($_) } @_;
return bless([EMAIL PROTECTED], $class);
}
@@ -24,7 +27,7 @@
{
my $queue = shift;
lock(@$queue);
- push(@$queue, map { $make_shared->($_) } @_)
+ push(@$queue, map { shared_clone($_) } @_)
and cond_signal(@$queue);
}
@@ -111,7 +114,7 @@
}
# Add new items to the queue
- push(@$queue, map { $make_shared->($_) } @_);
+ push(@$queue, map { shared_clone($_) } @_);
# Add previous items back onto the queue
push(@$queue, @tmp);
@@ -161,72 +164,6 @@
### Internal Functions ###
-# Create a thread-shared version of a complex data structure or object
-$make_shared = sub {
- my $item = shift;
-
- # If not running 'threads' or already thread-shared,
- # then just return the input item
- return $item if (! $threads::threads ||
- threads::shared::is_shared($item));
-
- # Make copies of array, hash and scalar refs
- my $copy;
- if (my $ref_type = Scalar::Util::reftype($item)) {
- # Copy an array ref
- if ($ref_type eq 'ARRAY') {
- # Make empty shared array ref
- $copy = &share([]);
- # Recursively copy and add contents
- push(@$copy, map { $make_shared->($_) } @$item);
- }
-
- # Copy a hash ref
- elsif ($ref_type eq 'HASH') {
- # Make empty shared hash ref
- $copy = &share({});
- # Recursively copy and add contents
- foreach my $key (keys(%{$item})) {
- $copy->{$key} = $make_shared->($item->{$key});
- }
- }
-
- # Copy a scalar ref
- elsif ($ref_type eq 'SCALAR') {
- $copy = \do{ my $scalar = $$item; };
- share($copy);
- # Clone READONLY flag
- if (Internals::SvREADONLY($$item)) {
- Internals::SvREADONLY($$copy, 1);
- }
- }
-
- # Copy of a ref of a ref
- elsif ($ref_type eq 'REF') {
- my $tmp = $make_shared->($$item);
- $copy = \$tmp;
- share($copy);
- }
- }
-
- # If no copy is created above, then just return the input item
- # NOTE: This will end up generating an error for anything
- # other than an ordinary scalar
- return $item if (! defined($copy));
-
- # Clone READONLY flag
- if (Internals::SvREADONLY($item)) {
- Internals::SvREADONLY($copy, 1);
- }
-
- # If input item is an object, then bless the copy into the same class
- if (my $class = Scalar::Util::blessed($item)) {
- bless($copy, $class);
- }
-
- return $copy;
-};
-
# Check value of the requested index
$validate_index = sub {
my $index = shift;
@@ -265,7 +202,7 @@
=head1 VERSION
-This document describes Thread::Queue version 2.06
+This document describes Thread::Queue version 2.08
=head1 SYNOPSIS
@@ -518,7 +455,7 @@
L<http://www.cpanforum.com/dist/Thread-Queue>
Annotated POD for Thread::Queue:
-L<http://annocpan.org/~JDHEDDEN/Thread-Queue-2.06/lib/Thread/Queue.pm>
+L<http://annocpan.org/~JDHEDDEN/Thread-Queue-2.08/lib/Thread/Queue.pm>
Source repository:
L<http://code.google.com/p/thread-queue/>
==== //depot/maint-5.8/perl/lib/Thread/Queue/t/02_refs.t#2 (text) ====
Index: perl/lib/Thread/Queue/t/02_refs.t
--- perl/lib/Thread/Queue/t/02_refs.t#1~33516~ 2008-03-13 12:37:42.000000000
-0700
+++ perl/lib/Thread/Queue/t/02_refs.t 2008-05-25 15:45:48.000000000 -0700
@@ -23,7 +23,7 @@
require Test::More;
}
Test::More->import();
-plan('tests' => 39);
+plan('tests' => 46);
# Regular array
my @ary1 = qw/foo bar baz/;
@@ -62,18 +62,34 @@
my $qux = \$baz;
is_deeply($$$$qux, $foo, 'Ref of ref');
+# Circular refs
+my $cir1;
+$cir1 = \$cir1;
+
+my $cir1s : shared;
+$cir1s = \$cir1s;
+
+my $cir2;
+$cir2 = [ \$cir2, { 'ref' => \$cir2 } ];
+
+my $cir3 :shared = &share({});
+$cir3->{'self'} = \$cir3;
+bless($cir3, 'Circular');
+
# Queue up items
my $q = Thread::Queue->new([EMAIL PROTECTED], [EMAIL PROTECTED]);
ok($q, 'New queue');
is($q->pending(), 2, 'Queue count');
$q->enqueue($obj1, $obj2);
is($q->pending(), 4, 'Queue count');
-$q->enqueue($sref1, $sref2, $qux);
-is($q->pending(), 7, 'Queue count');
+$q->enqueue($sref1, $sref2, $foo, $qux);
+is($q->pending(), 8, 'Queue count');
+$q->enqueue($cir1, $cir1s, $cir2, $cir3);
+is($q->pending(), 12, 'Queue count');
# Process items in thread
threads->create(sub {
- is($q->pending(), 7, 'Queue count in thread');
+ is($q->pending(), 12, 'Queue count in thread');
my $tary1 = $q->dequeue();
ok($tary1, 'Thread got item');
@@ -116,9 +132,38 @@
is($$tsref2, 69, 'Shared scalar ref contents');
$$tsref2 = 'zzz';
+ my $myfoo = $q->dequeue();
+ is_deeply($myfoo, $foo, 'Array ref');
+
my $qux = $q->dequeue();
is_deeply($$$$qux, $foo, 'Ref of ref');
+ my ($c1, $c1s, $c2, $c3) = $q->dequeue(4);
+ SKIP: {
+ skip("Needs threads::shared >= 1.19", 5)
+ if ($threads::shared::VERSION < 1.19);
+
+ is(threads::shared::_id($$c1),
+ threads::shared::_id($c1),
+ 'Circular ref - scalar');
+
+ is(threads::shared::_id($$c1s),
+ threads::shared::_id($c1s),
+ 'Circular ref - shared scalar');
+
+ is(threads::shared::_id(${$c2->[0]}),
+ threads::shared::_id($c2),
+ 'Circular ref - array');
+
+ is(threads::shared::_id(${$c2->[1]->{'ref'}}),
+ threads::shared::_id($c2),
+ 'Circular ref - mixed');
+
+ is(threads::shared::_id(${$c3->{'self'}}),
+ threads::shared::_id($c3),
+ 'Circular ref - hash');
+ }
+
is($q->pending(), 0, 'Empty queue');
my $nothing = $q->dequeue_nb();
ok(! defined($nothing), 'Nothing on queue');
==== //depot/maint-5.8/perl/lib/Thread/Semaphore.pm#5 (text) ====
Index: perl/lib/Thread/Semaphore.pm
--- perl/lib/Thread/Semaphore.pm#4~33516~ 2008-03-13 12:37:42.000000000
-0700
+++ perl/lib/Thread/Semaphore.pm 2008-05-25 15:45:48.000000000 -0700
@@ -3,7 +3,7 @@
use strict;
use warnings;
-our $VERSION = '2.07';
+our $VERSION = '2.08';
use threads::shared;
use Scalar::Util 1.10 qw(looks_like_number);
@@ -12,7 +12,10 @@
sub new {
my $class = shift;
my $val :shared = @_ ? shift : 1;
- if (! looks_like_number($val) || (int($val) != $val)) {
+ if (!defined($val) ||
+ ! looks_like_number($val) ||
+ (int($val) != $val))
+ {
require Carp;
$val = 'undef' if (! defined($val));
Carp::croak("Semaphore initializer is not an integer: $val");
@@ -25,7 +28,11 @@
my $sema = shift;
lock($$sema);
my $dec = @_ ? shift : 1;
- if (! looks_like_number($dec) || (int($dec) != $dec) || ($dec < 1)) {
+ if (! defined($dec) ||
+ ! looks_like_number($dec) ||
+ (int($dec) != $dec) ||
+ ($dec < 1))
+ {
require Carp;
$dec = 'undef' if (! defined($dec));
Carp::croak("Semaphore decrement is not a positive integer: $dec");
@@ -39,7 +46,11 @@
my $sema = shift;
lock($$sema);
my $inc = @_ ? shift : 1;
- if (! looks_like_number($inc) || (int($inc) != $inc) || ($inc < 1)) {
+ if (! defined($inc) ||
+ ! looks_like_number($inc) ||
+ (int($inc) != $inc) ||
+ ($inc < 1))
+ {
require Carp;
$inc = 'undef' if (! defined($inc));
Carp::croak("Semaphore increment is not a positive integer: $inc");
@@ -55,7 +66,7 @@
=head1 VERSION
-This document describes Thread::Semaphore version 2.07
+This document describes Thread::Semaphore version 2.08
=head1 SYNOPSIS
@@ -140,7 +151,7 @@
L<http://www.cpanforum.com/dist/Thread-Semaphore>
Annotated POD for Thread::Semaphore:
-L<http://annocpan.org/~JDHEDDEN/Thread-Semaphore-2.07/lib/Thread/Semaphore.pm>
+L<http://annocpan.org/~JDHEDDEN/Thread-Semaphore-2.08/lib/Thread/Semaphore.pm>
Source repository:
L<http://code.google.com/p/thread-semaphore/>
End of Patch.