RevBank::Abort
Leave a transaction from anywhere
Synopsis
use RevBank::Abort qw(abortable abort stop is_stop);
for (;;) {
if (my $abort = abortable { one_transaction() }) {
last if is_stop $abort;
clean_up(@$abort);
}
}
sub somewhere_deep_down {
abort "Nope."; # never returns
}
Description
abortable runs a block that any code called from it can leave by calling
abort, at any depth. That includes plugins and callbacks from C libraries.
It returns undef if the block finished, or abort's arguments as an array
reference if it did not.
stop aborts with a sentinel instead of a reason, to end the loop rather than
the transaction. is_stop tells the two apart.
Why this is not an exception
A user can abort from every prompt and at every word, so nothing may intercept
it. An exception would be caught by every eval on the way:
RevBank::Command's _call_plugin and RevBank::Plugins' call_hooks
would report it as a plugin failure, and a plugin's own eval or try would
have to know to rethrow it.
Loop control can't be caught. It also works from a callback called by XS, such
as libreadline's event hook, which is how ^C reaches the shell.
The cost is that nothing in an abortable block is sure to run to the end.
local and DESTROY still unwind normally.
Outside an abortable block
With no abortable on the stack, abort dies with the reason instead. That
happens in hooks that run before the shell, such as hook_startup, where a
plugin that refuses to start should say why, not cause a Perl error about a
missing label.
Author
Juerd Waalboer #####@juerd.nl