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


Hi all,

This is my virgin question to perlmonks!

I am aiming to write a funtion that impelements a timeout facility.

I have done all the code for the timeout with "eval" "$SIG{ALRM}" "alarm" and I have proven the actual timeout code.

The alarm gets set to a number of seconds, then the next bit of code is where I perform an operation that might hang the script.

What I would like to do is re-use the timeout code by putting it into a timeout funtion, where I pass in the timeout time, address of a funtion and any parameters that need to be passed to that funtion.

In the timeout function, I intend to do the "eval" "$SIG{ALRM}" "alarm" code and then call the funtion that the timeout funtion has been given the address of via the \&<function name> mechanism. This \&<function name> would be a function that might hang.

E.G.
sub timeout_func{ # The first parameter is the timeout time my $TIMEOUT=sprintf("%s",$_[0]); # The second parameter is the address of the function we # need to perform a timeout for my $ADDRESS=sprintf("%s",$_[1]); # The third parameter is the first parameter of the # function we need to run my $FIRST=sprintf("%s",$_[2]); eval{ #set the callback for the alarm signal local $SIG{ALRM} = sub { die ("ETIME\n") }; #specify the alarm timeout alarm($TIMEOUT); #call the funtion that the calling code has provided #the address of via the \&<function name> mechanism #if we came back with in the time limit, reset the alarm alarm(0); }; } # timeout_func timeout_func(120,\&funtion_that_might_hang,<1st param to funtion_that_might_hang>);


I realise that I stil need to do more work on this code, but the bit I
am hoping that someone can advise me on how to do the following:

#call the funtion that the calling code has provided the
#address of via the \&<function name> mechanism

Please could anybody let me know if they know how I should do this.

I know that the \&<function name> mechanism can be made to work becasue the threads and forks modules use this method.

If I can do this, then I can use this timeout funtion in various places in my scripts without duplicatind the same code.

Thanks in advance.

Richard Thomas

Replies are listed 'Best First'.
Re: Passing function to a function using \&<function name> method
by edan (Curate) on Dec 17, 2003 at 13:40 UTC

    Welcome to the Monastery!

    Something along these lines:

    timeout_func($timeout, \&myfunc, $arg1, $arg2); sub timeout_func { my ($timeout, $func, @args) = @_; # ... $func->(@args); }

    HTH

    FYI, that '\&<function name> mechanism' you're referring to is properly called a code reference (or 'coderef'). See perlref for more info.

    --
    3dan

      $func->(@args);

      Or if your code needs to run on older versions of Perl, such as 5.005 (update: 5.003), use the old-fashioned equivalent syntax &$func(@args);.

      Hi 3dan,

      Thanks very much for this advice, sorry I did not reply soon after your advice, I've just been sooo busy on other things at the moment.

      This method works exactly how I wanted, which is really great.

      There are a few things I would like a bit of advice on.

      1) When I set it up to timeout on a system command using the `<system command>` method, the timeout does not work.

      If I use the system(<system command>) method, it does work.

      I have demonstrated this to myself by writing a short .bat script that I have deliberately just written:
      FTP <hostIPaddress>

      This means that it will ask the user for username and password.

      As control it not returned to perl, it's up to the timeout to get perl to gain control again after the timeout value.

      Does anyone know why the timeout does not work when I use the `<system command>` for performing system commands?

      2) In the timeout_func function, is there a way of getting the function name from the coderef somehow, so that when this times out, I can log the function name of the function that has taken longer than the timeout value?

      Thanks for all your help.

        Does anyone know why the timeout does not work when I use the `<system command>` for performing system commands?

        No. I tested it with `cmd`, and it works fine for me. I am on Linux, and it sounds like you are on Win32, so maybe it's an OS thing with signals? *shrug*.

        In the timeout_func function, is there a way of getting the function name from the coderef somehow ... ?

        Not that I know of. You could get the name of the function from within the function itself with (caller 0)[3], but that won't help you in timeout_func (unless you set a package variable or something yucky like that). You could resort to using symbolic refs, though it's playing with fire...

        timeout_func($timeout, 'myfunc', ...); sub timeout_func { my ($timeout, $func_name, @args) = @_; # ... eval { # ... { no strict 'refs'; $func_name->(@args); } } if ($@) { die "$func_name died ($@)\n"; } }

        Notice that you pass the name of the function to call, instead of a hard reference to the function (the \&myfunc syntax).

        --
        edan (formerly known as 3dan)

        2) In the timeout_func function, is there a way of getting the function name from the coderef somehow, so that when this times out, I can log the function name of the function that has taken longer than the timeout value?
        See Re: Getting name of sub from a hash-based dispatch table? for a function to convert a coderef to sub name.
Re: Passing function to a function using \&<function name> method
by theorbtwo (Prior) on Dec 17, 2003 at 13:49 UTC

    3dan already answered your actual question: you want $coderef->(@args). I've just got a minor point of terminiology to clear up. \&foo does not return the address of &foo, it returns a reference to &foo.

    The difference is important, if somewhat subtle. The address of &foo is just a number, without intrinsic meaning. A reference to &foo "knows" what it's pointing at, and indeed, that it's pointing at something. It serves as an indicator to perl that what it's refering to is not garbage, and that it can't be thrown away just yet, while there is still a refernce to it.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: Passing function to a function using \&<function name> method
by revdiablo (Prior) on Dec 17, 2003 at 17:37 UTC

    3dan has already answered your question, and you will probably want to do it the way he showed. In the ever-present spirit of TIMTOWTDI, I figured I would show you another approach:

    timeout_func($timeout, sub { myfunc($arg1, $arg2) }); sub timeout_func { my ($timeout, $coderef) = @_; ... $coderef->(); }

    This creates an anonymous subroutine that does nothing but call your function with the given arguments. This is, incidentally, a nice way to get around an API that takes a coderef but doesn't take in "additional arguments" as 3dan's does, so the technique might be useful for you in the future.