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