NAME

Try::ALRM - Provides alarm semantics similar to Try::Catch.

SYNOPSIS

use Try::ALRM;
 
timeout 5;
try {
  local $|=1; #autoflush for STDOUT
  print qq{ doing something that might timeout ...\n};
  sleep 6;
}
ALRM {
  print qq{ Wake Up!!!!\n};
};

Is equivalent to,

local $SIG{ALRM} = sub { print qq{ Wake Up!!!!\n} };
alarm 5;
local $|=1; #autoflush for STDOUT
print qq{ doing something that might timeout ...\n};
sleep 6;
alarm 0; # reset alarm, end of 'try' block implies this "reset"

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 3 methods:

try
ALRM
timeout

PACKAGE ENVIRONMENT

This module exposes $Try::ALRM::TIMEOUT as a package variable; it can be modified in traditional ways. The module also provides ways to deal with it, continue reading to learn how.

The try keyword

This module has not yet been tested extensively with Try::Tiny, but in order to eliminate the potential for clobbering the exported try method, it may be prudent to refer to Try::ALRM's methods using their fully qualified package names, e.g.:

use strict;
use warnings;
use Try::ALRM qw/ALRM timeout/; #<~ NB!
use Try::Tiny;

# starts as default, $Try::ALRM::TIMEOUT;
printf qq{default timeout is %d seconds\n}, timeout;

# set timeout (persists)
timeout 5;
printf qq{timeout is set globally to %d seconds\n}, timeout;

# try/ALRM
Try::ALRM::try {
    local $| = 1;

    try {
      die qq{foo\n};
    }
    catch {
      print qq{$_\n};
    };

    # timeout is set to 1 due to trailing value after ALRM block
    printf qq{timeout is now set locally to %d seconds\n}, timeout;
    sleep 6;
}
ALRM {
    print qq{Alarm Clock!!\n};
} 1; # <~ temporarily overrides $Try::ALRM::TIMEOUT

printf qq{timeout is set globally to %d seconds\n}, timeout;

As this module matures, this will be explored more thoroughly; as proper deference should be given to Try::Tiny since it's such an extensively used module.

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

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 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 an ALRM is ssued.

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 using alarm 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 via die; the traditional signal handling mechanism is being invoked as the exception handler.

SETTING 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 value after the ALRM block
try {
  this_subroutine_call_may_timeout();
}
ALRM {
  print qq{ Alarm Clock!!!!\n};
} 10; # NB: no comma; 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 addition of this timeout affects $Try::ALRM::TIMEOUT for the duration of the try block, internally is using local to set $Try::ALRM::TIMEOUT. The reason for this is so that timeout may continue to function properly as a getter inside of the try block.

Example

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 {
  local $|=1;
  printf qq{ doing something that might timeout before %d seconds are up ...\n}, timeout;
  sleep 6;
}
ALRM {
  print qq{Alarm Clock!!\n};
} 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

Bugs

Very likey.

MMV. If found, please file issue on GH repo.

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.