# A thread-safe inside-out object class package SafeObject; use strict; use warnings; use Scalar::Util qw( refaddr weaken ); our $VERSION = 0.001; # Global object tracking and constructor my %REGISTRY; # Object property storage and accessor my %NAME; sub name { my ($self, $value) = @_; # store a value if one is provided my $id = refaddr $self; if ( defined $value ) { $NAME{ $id } = $value; } return $NAME{ $id }; } # Constructor and destructor sub new { my $class = shift; my $self = bless {}, $class; # store a weak reference in the registry my $id = refaddr $self; weaken ( $REGISTRY{ $id } = $self ); return $self; } sub DESTROY { my $self = shift; my $id = refaddr $self; # clean up memory used for the object delete $NAME{ $id }; delete $REGISTRY{ $id }; return; } # Cloning routine called for new threads sub CLONE { # So we can see this called in a Windows fork() warn "# Notice: Cloning data in new thread\n"; # fix-up all object ids in the new thread # (note: %REGISTRY change in the middle, so don't use "each") for my $old_id ( keys %REGISTRY ) { # look under old_id to find the new, cloned reference my $object = $REGISTRY{ $old_id }; my $new_id = refaddr $object; # relocate data $NAME{ $new_id } = $NAME{ $old_id }; delete $NAME{ $old_id }; # update the weak reference to the new, cloned object weaken ( $REGISTRY{ $new_id } = $REGISTRY{ $old_id } ); delete $REGISTRY{ $old_id }; } return; } 1; # package must return true #### #!/usr/bin/perl use strict; use warnings; use 5.008; # CLONE only supported in Perl > 5.8 use threads; use Test::More tests => 7; require_ok( "SafeObject" ); my $safe_obj = SafeObject->new; isa_ok( $safe_obj, "SafeObject" ); is( $safe_obj->name( "Charlie" ), "Charlie", "mutator returns value" ); is( $safe_obj->name() , "Charlie", "accessor returns value" ); my $thr = threads->new( sub { is( $safe_obj->name( ), "Charlie", "got right name in thread"); is( $safe_obj->name( "Fred" ), "Fred" , "changed name in thread" ); } ); $thr->join; is( $safe_obj->name(), "Charlie", "main thread still has original name" ); #### #!/usr/bin/perl use strict; use warnings; use 5.008; # CLONE only supported in Perl > 5.8 use Test::More tests => 7; require_ok( "SafeObject" ); my $obj = SafeObject->new; isa_ok( $obj, "SafeObject" ); is( $obj->name( "Charlie" ), "Charlie", "mutator returns value" ); is( $obj->name() , "Charlie", "accessor returns value" ); my $child_pid = fork; if ( !$child_pid ) { # we're in the child is( $obj->name( ), "Charlie", "got right name in child"); is( $obj->name( "Fred" ), "Fred" , "changed name in child" ); exit; } # wait for child to finish waitpid $child_pid, 0; # Test counter is off due to the fork Test::More->builder->current_test( 6 ); is( $obj->name(), "Charlie", "parent still has original name" );