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(); } } #### { 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(); } }