Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Win32: timing out a perl subroutine

by osfameron (Hermit)
on Jun 19, 2001 at 16:56 UTC ( [id://89618]=perlquestion: print w/replies, xml ) Need Help??

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

Similar topics have already been discussed relating to timing out on Perlmonks processes (e.g. the Net::Ping discussion) and user input (with Term::ReadKey etc.).

The problem is that alarm() is not implemented on Win32 Perl. Possible workarounds are forking off a subprocess (from Perl 5.6 possibled in Win32) and killing it if its still alive after timeout.

But I've not come across any discussion (and apologies if I've just missed it) about timing out a Perl sub. That is, I want to get back the result of an arbitrary subrouting call - so I can't fork off a disassociated process.

# example snippet. Doesn't refer to a real function yet... my $result = timeout ( time =>1.5, sub =>sub { # do something complicated here # that may take a long time return $whatever; }, timed_out => "Function timed out" );
e.g. $result should either return the result of the sub in the code reference, or the value of the 'timed_out' parameter (most likely undef).

What I've tried

I tried using threads, because I'd understood that this was how Win32 'fork' emulation was done... but I get a "No threads in this Perl" error. (perl -v gives me "v5.6.0 built for MSWin32-x86-multi-thread ... Binary build 616 provided by ActiveState")

I thought of the following:
- fork child
- parent runs sub
- parent then kills child
- child sleeps till timeout
- child wakes up (unless it's been killed)
- child kills parent. (e.g. the child then takes over as a new parent process, as with daemonization)

But I don't think a child can kill the parent? Alternatively, the child could flag to the parent that it will now take over thank you very much, so that if/when the parent returns it will just exit. But I'm confused on the details of IPC. I've read perlipc, and it's very inspiring, but as 60-70% of it is Unix-only, it's a little confusing for us Win32 types.
(Anyone know of any good Win32 Perl IPC tutorials out there?)

So does anyone have any suggestions, even pointers of where to rtfm?

Cheerio!
Osfameron

Replies are listed 'Best First'.
Re: Win32: timing out a perl subroutine
by frag (Hermit) on Jun 19, 2001 at 20:11 UTC
    Have you looked at Win32::Process? If I'm interpreting you correctly, this seems to give you what you want:
    use Win32::Process; Win32::Process::Create($ProcessObj, "c:\\Perl\\bin\\perl.exe", "perl -e \"print q(yr code here\n)\"", 1, NORMAL_PRIORITY_CLASS, ".") $a = $ProcessObj->Wait(1000); # units = ms $ProcessObj->Kill($some_exit_code); print "[$a] ==> I *think* this will be zero if the " . " process terminated on its own before Wait() finished\n";
    If the code in the $ProcessObj calls exit() on its own, the exit value will be retreivable via:
    $ProcessObj->GetExitCode($exitvalue); print "The code returned: $exitvalue\n";
    Not the most elegant solution (what with having to respecify the path for perl.exe, and I assume it's more memory intensive) but that's Win32 for you.

    N.B.: That '1' means that the new process inherits the same filehandles as the code that produces it. If you don't want that, change it to '0'.

    There's a lot of additional flags besides NORMAL_PRIORITY_CLASS. In particular, you can use

    DETACHED_PROCESS CREATE_NEW_CONSOLE DETACHED_PROCESS | IDLE_PRIORITY_CLASS
    (Note that's a bitwise or in the last example.) These flags are really poorly documented - I recommend finding one of Dave Roth's Win32 books).

    -- Frag.

      Thanks: I agree that the Win32::Process module is really badly documented - Dave Roth's documentations is much better.

      <Aside>and that's an issue for many of the Win32 modules... For example, Win32::Console is a good module, but it makes me tear my hair out trying to get it to do what I want: if I ever have time I'd re-document it.</Aside>

      But though that's a useful code snippet which I'll file away for future use, it doesn't allow me to return a value from a subroutine within the current perl script. Unix users, using the alarm() implementation of timeouts, have no problem doing a pre-emptive timeout of any arbitrary code.

      I'd be interested to hear from other my $os!~/l?[ui]n[ui]x/ users about how/if they can do timeouts on their perl.

      Cheerio!
      Osfameron

        I've posted a module for review which is a very scrappy and buggy first try at implementing this sort of timeout. Comments/review welcome!

        Update Well no comments as yet, but I've found even more bugs... hey ho

        Cheerio!
        Osfameron

Re: Win32: timing out a perl subroutine
by Brovnik (Hermit) on Jun 19, 2001 at 19:18 UTC
    You can implement your own non pre-emptive timer fairly easily :

    #! /usr/local/bin/perl -w use strict; { my $timesup = undef; sub timeout { if (@_) { # set the time to timeout on $timesup = time() + shift; } else { # return true as soon as we go over the limit return time() > $timesup; } } } # Programs code here timeout(10); # set to timeout in 10 seconds. my $timedout = 0; for (1..15) { # check if we can continue # If not, set flag to check later $timedout++ && last if timeout(); print "Do complicated thing, iter $_\n"; sleep(1); # pretend to do hard stuff } if ($timedout) { print "operation timedout\n"; } else { print "Success, complicated stuff completed\n"; }
    Of course, being non-preemptive, this doesn't guarantee you'll get control back within a certain time, if the loop iteration takes to long, or the process blocks (e.g. waiting for user input).

    Also assumes that other functions like alarm() aren't implemented, since these would be more robust, but the above is lightweight and may do what you need.

    Update: I meant nonpre-emptive.
    --
    Brovnik

Re: Win32: timing out a perl subroutine
by Eradicatore (Monk) on Jun 19, 2001 at 18:08 UTC
    Here is just a few quick thoughts I had. I know you can pass a timeout to things like http requests using LWP on windows. I'm not sure if they work for LWP on windows, but if they do, you can just go look at their code (why reinvent it?). If you installed activestate perl, then you should find that code in c:\Perl\site\lib\LWP unless you installed it custom on your own.

    Also, since fork is working on one of the latest perl releases by activestate on win 32 (see my post Re: Threads) you could do what you said and fork off the process, and have the parent poll for the child to return something on the return pipe, or if it didn't do it in time (use could use just use sleep in a loop to poll over certain time intervals) just kill the child. It sounds like you were having problems with forking in win32, so that's why I point out that node. Check out my other nodes at Eradicatore for more on win32 and forking. I've been posting a lot on that lately too.

    oh, also look at IO::Socket::INET to see if there is good examples of using timeouts. Again, this may or may not be working on win32, but it's worth a shot to see if they already got this working.

    Justin Eltoft

    "If at all god's gaze upon us falls, its with a mischievous grin, look at him" -- Dave Matthews

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://89618]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-23 10:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found