http://www.perlmonks.org?node_id=1045532

MiniMizer has asked for the wisdom of the Perl Monks concerning the following question:

Hi,
I'm trying to find a way to send arguments to a subroutine which I'm referencing to via SIG{"ALRM"}.


i.e.
SIG{"ALRM"}=\&Timeout($error);

I have a single subroutine that handled the timeouts, but I want it to get a different arguments based on the bit of code where the timeout occured, for instance.

Is this something that can be done? I haven't been able to get it to work using the above syntax.

Replies are listed 'Best First'.
Re: Passing arguments to a function reference
by choroba (Cardinal) on Jul 21, 2013 at 20:06 UTC
    If a subroutine reference is expected, you have to provide a wrapper subroutine to give it arguments:
    SIG{ALRM} = sub { Timeout($error) };
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Passing arguments to a function reference
by BrowserUk (Patriarch) on Jul 21, 2013 at 20:13 UTC

    What are you expecting the value of $error to be when the signal arrives?

    Plus, why not simply capture the variable as a closure:

    my $error; sub timeout { ... if( $error ) ... ... } ... $SIG{ALRM} = \&timeout;

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
    /div
Re: Passing arguments to a function reference
by rjt (Curate) on Jul 21, 2013 at 22:04 UTC
    SIG{"ALRM"}=\&Timeout($error);

    I have a single subroutine that handled the timeouts, but I want it to get a different arguments based on the bit of code where the timeout occured, for instance.

    Even if you wrap the signal handler in a sub that passes $error, you will probably have a tough time populating $error with any meaningful information when the timeout occurs. However, since you say you want information "based on the bit of code where the timeout occurred", you can achieve that with caller:

    #!/usr/bin/env perl use 5.012; use warnings; # Convenience map for caller return my @ckeys = qw< pack file line sub has_args wantarray evaltext is_require hints bitmask hinthash >; sub Timeout { say "\n===> Signal caught! Call stack below <===="; for (my $i = 1; ;$i++) { my %call; @call{ @ckeys } = caller $i; last if not defined $call{pack}; # Done $call{wantarray} = $call{wantarray} ? 'LIST' : defined $call{wantarray} ? 'SCALAR' : 'VOID'; printf "%20s:%-4d %6s %s\n", @call{qw<file line wantarray sub> +}; } } $SIG{"ALRM"} = \&Timeout; kill ALRM => $$; my @foo = one(); two(); sub one { kill ALRM => $$; } sub two { one('from_two'); }

    Output:

    ===> Signal caught! Call stack below <==== /tmp/uaVo7iA.pl:26 SCALAR (eval) ===> Signal caught! Call stack below <==== /tmp/uaVo7iA.pl:31 SCALAR (eval) /tmp/uaVo7iA.pl:27 LIST main::one ===> Signal caught! Call stack below <==== /tmp/uaVo7iA.pl:31 SCALAR (eval) /tmp/uaVo7iA.pl:35 VOID main::one /tmp/uaVo7iA.pl:28 VOID main::two
      caller() is indeed the correct solution, but I prefer to use Devel::StackTrace, which is a nice wrapper around it. caller() has a hideous interface, Devel::StackTrace has quite a nice one.
        Thanks for all the help guys, I'll look into caller.