use
version 0.9915;
our
$VERSION
= version->declare(
'1.0.0'
);
BEGIN {
for
(
qw( alarm time )
) {
Time::HiRes->
import
(
$_
)
if
Time::HiRes->can(
$_
);
}
}
our
@EXPORT_OK
=
qw( timeout )
;
sub
timeout( $@ ) {
my
$context
=
wantarray
;
my
$timeout
= assert_NonNegativeNumber
shift
;
my
$code
= assert_CodeRef
pop
;
my
@code_args
=
@_
;
my
$exception
;
my
@result
;
my
$remaining_time_on_previous_timer
=
alarm
0;
my
$start_time
=
time
;
{
local
$SIG
{ ALRM } =
'IGNORE'
;
try
{
local
$SIG
{ ALRM } =
sub
{
die
Time::Out::Exception->new(
previous_exception
=> $@,
timeout
=>
$timeout
) };
if
(
$remaining_time_on_previous_timer
and
$remaining_time_on_previous_timer
<
$timeout
) {
alarm
$remaining_time_on_previous_timer
;
}
else
{
alarm
$timeout
;
}
defined
$context
?
$context
?
@result
=
$code
->(
@code_args
)
:
$result
[ 0 ] =
$code
->(
@code_args
)
:
$code
->(
@code_args
);
alarm
0;
}
finally
{
alarm
0;
$exception
=
$_
[ 0 ]
if
@_
;
}
}
my
$elapsed_time
=
time
-
$start_time
;
my
$new_timeout
=
$remaining_time_on_previous_timer
-
$elapsed_time
;
if
(
$new_timeout
> 0 ) {
alarm
$new_timeout
;
}
elsif
(
$remaining_time_on_previous_timer
) {
kill
'ALRM'
, $$;
}
if
(
defined
$exception
) {
if
(
defined
blessed(
$exception
) and
$exception
->isa(
'Time::Out::Exception'
) ) {
$@ =
$exception
;
return
;
}
die
$exception
;
}
return
defined
$context
?
$context
?
return
@result
:
$result
[ 0 ]
: ();
}
1;