It seems to me you're heading down the wrong path. If we knew the big picture better (What are these package vars? Why do they need to restore them?), perhaps you could help you better.
Or maybe it's as simple as moving the variables from the package to a hash or object. Then you could dclone or freeze the hash to make a "backup" of it.
Here's an implementation that uses a hash:
use Scope::Guard qw( );
my %state;
{
my $backup = freeze(\%state);
my $sentinel = Scope::Guard->new(sub {
%state = %{ thaw($backup) };
});
$state{a} = ...;
push @{$state{b}}, ...;
$state{c}{key} = ...;
if (success) {
$sentinel->dismiss();
}
}
The advantage of using Scope::Guard is that the changes to %state will get undone automatically if you return or die before reaching the end of the block.
Update: Here's the class version:
{
package AtomicState; # yucky name!
sub new {
my ($class) = @_;
return bless({}, $class);
}
sub transaction {
my ($self) = @_;
return AtomicState::Transaction->new($self);
}
}
{
package AtomicState::Transaction;
sub new {
my ($class) = @_;
my $obj_ref = \$_[1];
my $self = bless([ $obj_ref, undef ], $class);
$self->commit();
return $self;
}
sub start_transaction {
my ($self) = @_;
my $backup = freeze($self);
return AtomicState::Transaction->new($self);
}
# Multiple commits are allowed.
sub commit {
my ($self) = @_;
$self->[1] = freeze(${$self->[0]});
}
# Rolls back to the last commit, the first
# being the creation of the the Transaction.
sub rollback {
my ($self) = @_;
${$self->[0]} = thaw($self->[1]);
}
sub DESTROY {
my ($self) = @_;
$self->rollback();
}
}
my $state = AtomicState->new();
{
my $transact = $state->transaction();
$state->{a} = ...;
push @{$state->{b}}, ...;
$state{c}->{key} = ...;
if (success) {
$transact->commit();
}
}
|