NAME
Try::ALRM - Provides try_once
and retry
semantics to CORE::alarm
, similar to Try::Catch
.
FRIENDLY TESTING AND FEEDBACK REQUESTED
While the utility of this module should be clear, there are a few factors that require a maturing. These issues are addressed throughout the documentation below. Using the module and providing feedback about it will be extremely appreciated. Please do so at the Github repo.
SYNOPSIS
try_once
use Try::ALRM;
try_once {
my ($attempts) = @_; # @_ is populated as described in this line
print qq{ doing something that might timeout ...\n};
sleep 6;
}
ALRM {
my ($attempts) = @_; # @_ is populated as described in this line
print qq{ Wake Up!!!!\n};
}
finally {
my ( $attempts, $success ) = @_; # Note: @_ is populated as described in this line when called with retry
my $tries = tries; # "what was the limit on number of tries?" Here it will be 4
my $timeout = timeout; # "what was the timeout allowed?" Here it will be 3
printf qq{%s after %d of %d attempts (timeout of %d)\n}, ($success) ? q{Success} : q{Failure}, $attempts, $tries, $timeout;
} timeout => 1;
Is essentially equivalent to,
local $SIG{ALRM} = sub { print qq{ Wake Up!!!!\n} };
alarm 1;
print qq{ doing something that might timeout ...\n};
sleep 6;
alarm 0; # reset alarm, end of 'try' block implies this "reset"
retry
# Note, the example above of 'try_once' is a reduced case of
# 'retry' with 'tries => 1'
retry {
my ($attempts) = @_; # @_ is populated as described in this line
printf qq{Attempt %d/%d ... \n}, $attempts, tries;
sleep 5;
}
ALRM {
my ($attempts) = @_; # @_ is populated as described in this line
printf qq{\tTIMED OUT};
if ( $attempt < tries ) {
printf qq{ - Retrying ...\n};
}
else {
printf qq{ - Giving up ...\n};
}
}
finally {
my ( $attempts, $success ) = @_; # Note: @_ is populated as described in this line when called with retry
my $tries = tries; # "what was the limit on number of tries?" Here it will be 4
my $timeout = timeout; # "what was the timeout allowed?" Here it will be 3
printf qq{%s after %d of %d attempts (timeout of %d)\n}, ($success) ? q{Success} : q{Failure}, $attempts, $tries, $timeout;
}
timeout => 3, tries => 4;
This is equivalent to ... well, checkout the implementation of Try::ALRM::retry(&;@)
, because it is equivalent to that :-).
However, it should be pointed out that try_once
is a reduced case of retry
where tries => 1
. There might be benefits to using retry
in this way.
DESCRIPTION
Provides try/catch-like semantics for handling code being guarded by alarm
. Because it's localized and probably expected, ALRM
signals can be treated as exceptions.
alarm
is extremely useful, but it can be cumbersome do add in code. The goal of this module is to make it more idiomatic, and therefore more accessible. It also allows for the ALRM
signal itself to be treated more semantically as an exception. Which makes it a more natural to write and read in Perl.
Internally, the keywords are implemented as prototypes and uses the same sort of coersion of a lexical bloc to a subroutine reference that is used in Try::Tiny
.
EXPORTS
This module exports 6 methods:
NOTE: Try::ALRM::try_once
and Try::ALRM::retry
are mutually exclusive, but one of them is required to invoke any benefits of using this module.
try_once BLOCK
-
Not meant to be used with
Try::ARLM::retry
.Primary BLOCK, attempted once with a timeout set by
$Try::ALRM::TIMEOUT
. If anALRM
signal is sent, the BLOCK described byALRM
will be called to handle the signal. IfALRM
is not defined, the normal mechanisms of handling$SIG{ALRM}
will be employed. Mutually exclusive ofretry
.Accepts blocks:
ALRM
,finally
; and trailing modifiertimeout => INT
.Note: that
try_once
is essentially a trival case ofretry
withtries => 1
; and in the future it may just become a wrapper around this case. For now it is its own independant implementation. retry BLOCK
-
Not meant to be used with
Try::ARLM::try
.Primary BLOCK, attempted
$Try::ALRM::TRIES
number of times with a timeout governed by$Try::ALRM::TIMEOUT
. If anALRM
signal is sent and the number oftries
has not been exhausted, theretry
BLOCK will be tried again. This continues until anALRM
signal is not triggered or if the number of$Try::ALRM::TRIES
has been reached.Accepts blocks:
ALRM
,finally
; and trailing modifierstimeout => INT
, andretries => INT
.retry
makes values available to eachBLOCK
that is called via@_
, see description of each BLOCK below for more details. This also applies to the BLOCK provided forretry
. ALRM BLOCK
-
Optional.
Called when an
ALRM
signal is detected. If noALRM
BLOCK is defined, then the default$SIG{ALRM}
handler mechanism is invoked.When called with
retry
,@_
contains the number of attempts that have been made so far.retry { ... } ALRM { my ($attempts) = @_; };
finally BLOCK
-
Optional.
This BLOCK is called unconditionally. When called with
try_once
,@_
contains an indication there being a timeout or not in the attempted block.When called with
retry
,@_
also contains the number of attempts that have been made before the attempts ceased. There is also a value that is passed that indicates ifALRM
had been invoked;... finally { my ($tries, $succeeded) = @; };
When used with
try_once
,@_
is empty. Note thattry_once
is essentially a trival case ofretry
withtries => 1
; and in the future it may just become a wrapper around this case. timeout INT
-
Setter/getter for
$Try::ALRM::TIMEOUT
, which governs the default timeout in number of seconds. This can be temporarily overridden using the trailing modifiertimeout => INT
that is supported viatry_once
andretry
.timeout 10; # sets $Try::ALRM::TIMEOUT to 10 try_once { ... } ALRM { my ($attempts) = @_; };
Can be overridden by trailing modifier,
timeout => INT
. tries INT
-
Setter/getter for
$Try::ALRM::TRIES
, which governs the number of attemptsretry
will make before giving up. This can be temporarily overridden using the trailing modifiertries => INT
that is supported viaretry
.timeout 10; # sets $Try::ALRM::TIMEOUT to 10 tries 12; # sets $Try:::ALRM::TRIES to 12 retry { ... } ALRM { my ($attempts) = @_; };
Can be overridden by trailing modifier,
tries => INT
.
PACKAGE ENVIRONMENT
This module exposes $Try::ALRM::TIMEOUT
and $TRY::ALRM::TRIES
as a package variables; it can be modified in traditional ways. The module also provides ways to deal with it, continue reading to learn how.
USAGE
Try::ALRM
doesn't really have options, it's more of a structure. So this section is meant to descript that structure and ways to control it.
try_once
-
This familiar idiom include the block of code that may run longer than one wishes and is need of an
alarm
signal.# default timeout is $Try::ALRM::TIMEOUT try { this_subroutine_call_may_timeout(); };
If just
try_once
is used here, what happens is functionall equivalent to:alarm 60; # e.g., the default value of $Try::ALRM::TIMEOUT this_subroutine_call_may_timeout(); alarm 0;
And the default handler for
$SIG{ALRM}
is invoked if anALRM
is ssued. retry
-
# default timeout is $Try::ALRM::TIMEOUT # default number of tries is $Try::ALRM::TRIES retry { this_subroutine_call_may_timeout_and_we_want_to_retry(); };
ALRM
-
This keyword is for setting
$SIG{ALRM}
with the block that gets passed to it; e.g.:# default timeout is $Try::ALRM::TIMEOUT try { this_subroutine_call_may_timeout(); } ALRM { print qq{ Alarm Clock!!!!\n}; };
The addition of the
ALRM
block above is functionally equivalent to the typical idiom of usingalarm
and setting$SIG{ALRM}
,local $SIG{ALRM} = sub { print qq{ Alarm Clock!!!!\n} }; alarm 60; # e.g., the default value of $Try::ALRM::TIMEOUT this_subroutine_call_may_timeout(); alarm 0;
So while this module present
alarm
with try/catch semantics, there are no actualy exceptions getting thrown viadie
; the traditional signal handling mechanism is being invoked as the exception handler.
TRAILING MODIFIERS
Setting the Timeout
The timeout value passed to alarm
internally is controlled with the package variable, $Try::ALRM::TIMEOUT
. This module presents 2 different ways to control the value of this variable.
timeout
-
Due to limitations with the way Perl prototypes work for creating syntactical structures, the most idiomatic solution is to use a setter/getter function to update the package variable:
timeout 10; # changes $Try::ALRM::TIMEOUT to 10 try { this_subroutine_call_may_timeout(); } ALRM { print qq{ Alarm Clock!!!!\n}; };
If used without an input value,
timeout
returns the current value of$Try::ALRM::TIMEOUT
. - Trailing after the last BLOCK
-
try { this_subroutine_call_may_timeout(); } ALRM { print qq{ Alarm Clock!!!!\n}; } timeout => 10; # NB: applies temporarily!
This approach utilizes the effect of defining a Perl prototype,
&
, which coerces a lexical block into a subroutine reference (i.e.,CODE
). The key => value syntax was chosen as a compromise because it makes things a lot more clear and makes the implementation of the blocks a lot easier (use the source to see how, Luke).The addition of this timeout affects $Try::ALRM::TIMEOUT for the duration of the
try_once
block, internally is usinglocal
to set$Try::ALRM::TIMEOUT
. The reason for this is so thattimeout
may continue to function properly as a getter inside of thetry_once
block.
try_once
/ALRM
/finally
Examples
Using the two methods above, the following code demonstrats the usage of timeout
and the effect of the trailing timeout value,
# set timeout (persists)
timeout 5;
printf qq{now %d seconds timeout\n}, timeout;
# try/ALRM
try {
printf qq{ doing something that might timeout before %d seconds are up ...\n}, timeout;
sleep 6;
}
ALRM {
print qq{Alarm Clock!!\n};
} timeout => 1; # <~ trailing timeout
# will still be 5 seconds
printf qq{now %d seconds timeout\n}, timeout;
The output of this block is,
default timeout is 60 seconds
timeout is set globally to 5 seconds
timeout is now set locally to 1 seconds
Alarm Clock!!
timeout is set globally to 5 seconds
Setting the Number of Tries
The number of total attempts made by retry
is controlled by the package variable, $Try::ALRM::TRIES
. And it provides similar controls to what is provided for controlling the timeout.
- Using the
tries
keyword will affect the package variable$Try::ALRM::TRIES
if passed an integer value. If passed nothing, the current value of$Try::ALRM::TRIES
will be returned - Trailing value after the last BLOCK
-
An example is best here,
retry { ... } timeout => 10, tries => 5;
Using the trailing values in this way allows the number of attempts to be temporarily set to the RHS value of
tries =>
.
Bugs
Very likey.
Milage May Vary, as they say. If found, please file issue on GH repo.
The module's purpose is essentially complete, and changes that are made will be strictly to fix bugs in the code or POD. Please report them, and I will find them eventually.
AUTHOR
oodler577
ACKNOWLEDGEMENTS
"To the least of you among of all of us. You make more of a difference than any of you will ever know." -Anonymous
COPYRIGHT AND LICENSE
Copyright (C) 2022 by oodler577
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.30.0 or, at your option, any later version of Perl 5 you may have available.