package Coro::LocalScalar;
use strict;
no strict 'refs';
use Guard;
use Carp;
use Scalar::Util qw/weaken/;
our $VERSION = '0.1';
=head1 NAME
Coro::LocalScalar - local() for Coro
=head1 ABOUT
Perl local() function unuseful for Coro threads. This module uses tie magick to make scalar local for each Coro thread.
=head1 SYNOPSIS
use Coro;
use Coro::LocalScalar;
use Coro::EV;
my $scalar;
Coro::LocalScalar->new->localize($scalar);
async {
$scalar = "thread 1";
print "1 - $scalar\n";
cede;
print "3 - $scalar\n";
cede;
print "5 - $scalar\n";
};
async {
$scalar = "thread 2";
print "2 - $scalar\n";
cede;
print "4 - $scalar\n";
cede;
print "6 - $scalar\n";
};
EV::loop;
prints
1 - thread 1
2 - thread 2
3 - thread 1
4 - thread 2
5 - thread 1
6 - thread 2
my $obj = Coro::LocalScalar->new;
# no tie magick used
$obj->value("data");
$obj->value = "data";
my $value = $obj->value;
#or
my $local_lvalue_closure = $obj->closure; # lvalue coderef
$local_lvalue_closure->() = "local data"; # no tie magick used
my $testobj = Someclass->new;
# attach setter/getter and tied hash element to your object
$obj->attach($testobj, 'element_local_in_coros');
$testobj->element_local_in_coros("data");
$testobj->element_local_in_coros = "data";
$testobj->{element_local_in_coros}; # tie magick used
=cut
sub new { bless {} }
sub TIESCALAR {$_[1]};
sub closure {
my $self = shift;
return sub :lvalue {
$self->get;
}
}
sub localize { # tie scalar to container
my $self = shift;
$self->{scalar} = \$_[0];
weaken $self->{scalar}; # no circular
tie($_[0], __PACKAGE__, $self );
$self;
}
sub attach { # attach setter/getter and tied hash element to class
# $container->attach($comeobj, 'db_conn'); $comeobj->{db_conn} and $comeobj->db_conn now local in Coros
*{ref($_[1]).'::'.$_[2]} = $_[0]->closure;
$_[1]->{$_[2]} = undef unless exists $_[1]->{$_[2]};
$_[0]->localize($_[1]->{$_[2]});
$_[0];
}
sub _proto : lvalue {
my $self = shift;
unless(exists($self->{$Coro::current})){
my $coro = $Coro::current;
$self->{$coro} = undef;
# init coro data destructor
$coro->on_destroy(sub {
${ $self->{scalar} } = undef; # Delete scalar value to prevent unexpected behavior
#perl stores values in scalar even if it tied. When Coro::LocalScalar destroys internal value, value stored in scalar still persists although you expected that it would be deleted( and DESTROY called if it`s object)
delete $self->{$coro};
undef $coro;
});
};
if(@_){ $self->{$Coro::current} = shift }
$self->{$Coro::current};
}
*STORE = *_proto;
sub FETCH : lvalue { shift->{$Coro::current} };
our $AUTOLOAD;
sub AUTOLOAD :lvalue { *{$AUTOLOAD} = *_proto; shift->_proto }
sub DESTROY {}
sub UNTIE {};
1;