Change 33882 by [EMAIL PROTECTED] on 2008/05/20 14:05:51
Affected files ...
... //depot/maint-5.10/perl/MANIFEST#23 integrate
... //depot/maint-5.10/perl/ext/threads/shared/Makefile.PL#4 integrate
... //depot/maint-5.10/perl/ext/threads/shared/shared.pm#5 integrate
... //depot/maint-5.10/perl/ext/threads/shared/shared.xs#4 integrate
... //depot/maint-5.10/perl/ext/threads/shared/t/clone.t#1 branch
... //depot/maint-5.10/perl/ext/threads/shared/t/stress.t#3 integrate
... //depot/maint-5.10/perl/ext/threads/shared/t/sv_refs.t#2 integrate
Differences ...
==== //depot/maint-5.10/perl/MANIFEST#23 (text) ====
Index: perl/MANIFEST
--- perl/MANIFEST#22~33823~ 2008-05-12 03:24:27.000000000 -0700
+++ perl/MANIFEST 2008-05-20 07:05:51.000000000 -0700
@@ -1128,6 +1128,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.10/perl/ext/threads/shared/Makefile.PL#4 (xtext) ====
Index: perl/ext/threads/shared/Makefile.PL
--- perl/ext/threads/shared/Makefile.PL#3~33521~ 2008-03-13
16:17:54.000000000 -0700
+++ perl/ext/threads/shared/Makefile.PL 2008-05-20 07:05:51.000000000 -0700
@@ -65,6 +65,7 @@
'Config' => 0,
'Carp' => 0,
'XSLoader' => 0,
+ 'Scalar::Util' => 0,
'Test' => 0,
'Test::More' => 0,
==== //depot/maint-5.10/perl/ext/threads/shared/shared.pm#5 (text) ====
Index: perl/ext/threads/shared/shared.pm
--- perl/ext/threads/shared/shared.pm#4~33521~ 2008-03-13 16:17:54.000000000
-0700
+++ perl/ext/threads/shared/shared.pm 2008-05-20 07:05:51.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.10/perl/ext/threads/shared/shared.xs#4 (text) ====
Index: perl/ext/threads/shared/shared.xs
--- perl/ext/threads/shared/shared.xs#3~33520~ 2008-03-13 15:43:35.000000000
-0700
+++ perl/ext/threads/shared/shared.xs 2008-05-20 07:05:51.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.10/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-20 07:05:51.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.10/perl/ext/threads/shared/t/stress.t#3 (text) ====
Index: perl/ext/threads/shared/t/stress.t
--- perl/ext/threads/shared/t/stress.t#2~33521~ 2008-03-13 16:17:54.000000000
-0700
+++ perl/ext/threads/shared/t/stress.t 2008-05-20 07:05:51.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.10/perl/ext/threads/shared/t/sv_refs.t#2 (text) ====
Index: perl/ext/threads/shared/t/sv_refs.t
--- perl/ext/threads/shared/t/sv_refs.t#1~32694~ 2007-12-22
01:23:09.000000000 -0800
+++ perl/ext/threads/shared/t/sv_refs.t 2008-05-20 07:05:51.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
End of Patch.