Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

'better mousetrap': how to perform timed event

by snafu (Chaplain)
on Apr 23, 2003 at 01:59 UTC ( [id://252427]=perlquestion: print w/replies, xml ) Need Help??

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

I am tailing a file with my program. Now, when I ''see'' a certain event I want to wait for $map_vTime seconds from the time that event was seen and then perform an action while all the while the loop continues to run.

Now, if the loop runs, great! My current way of doing this is for each iteration of the loop I update a hash %timer with the keys 'start', 'elapsed', and 'stop' where 'stop' is boolean, 'start' is when the event triggered, and 'elapsed' is (time() - 'start') (all done in seconds of course).

Here is my problem, since the loop is read only when something happens in the file (its a logfile for a game), theoretically, the timer could go on for n seconds (no players for example) before something gets read from the file again...way past a, lets say, 20 second timer which is the value I am using now.

So, this is not the behaviour I want. In fact, I tried using alarm() tied to signal handler. However, as in times past, I have encountered that unless the process is active time relative to alarm() doesn't actually remain constant or exact for that matter.

I considered using threads. However, giving the rough background and stern recommendations on the use of threads vs the current version of whatever Perl interpretter is installed on the box(es) I am not sure that using threads would be portable enough for me. In fact, two of the servers I am going to run this program on have 5.6.1 on them. I have direct root access control over one of these servers. When I attempt to install Thread.pm CPAN promptly states I need to upgrade to 5.8. Since I don't have the root access over one of the machines, it is not going to be an option for me to upgrade the interpretter.

So, to recap, Im trying to figure out the best way to perform an accurate timed event in a procedural manner that is portable to Perl 5.6.1 within a loop that is not constant in iteration.

Any thoughts? Time::HiRes? I have been looking into this module but I am not positive it has what I need.

TIA peeps.

_ _ _ _ _ _ _ _ _ _
- Jim
Insert clever comment here...

  • Comment on 'better mousetrap': how to perform timed event

Replies are listed 'Best First'.
Re: 'better mousetrap': how to perform timed event
by tachyon (Chancellor) on Apr 23, 2003 at 02:09 UTC

    Why not just fork off a process to wait around and do the stuff?

    # main loop while (1) { # do stuff # check for event my $event = check_for_event(); # now check for event and fork a child to deal with it # fork returns 0 to child, child PID to parent, undef if fails if ( $event and fork ==0 ) { # this is a child process sleep $whatever; do_stuff($event); # kid has done what was required so kill it exit 0; } # parent is continuing here, kid never gets here }

    Obviously once check_for_event() has returned a true value for an event once you want to make sure it returns false for that event the next call or you will fork() your system to death. Depending on the frequency of events and the sleep time you can potentially have a steadily increasing burden of waiting kids build up.

    Another option is to have a second parallel process to which you can send instructions. This process is in and endless loop waiting for instructions and executing them. You would probably pass it the instruction and an exec time which it stores in memory/file/DB and queries as required. Once an intruction has been processed you delete it.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      I thought of this approach but wasn't able to come up with a really good way for the parent to know when the child finished counting. I am guessing that I would perform something to find out when the child is reaped (supposing it exits when the timer is finished?).

      I apologize for some of the misunderstanding. This goes into somethings that have never been quite clear to me.

      _ _ _ _ _ _ _ _ _ _
      - Jim
      Insert clever comment here...

        You can find out exactly when the child exits but this is not what you specified which was exec something X seconds after Y event occurs. Forking a kid will do that for you and is quite portable. Why do you need to know when the child finishes? Not in spec. Kid will do its stuff X seconds after Y event. Why monitor it? What would you do if the child failed? If nothing redo Does it really matter? If no redo. What is failure? etc.....

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

(USING POE!) Re: 'better mousetrap': how to perform timed event
by eduardo (Curate) on Apr 23, 2003 at 16:05 UTC
    It's time for everyone's favorite "i love my hammer, everything looks like a nail" post :) I have sung the praises of POE numerous times, and I am here once again to do so. POE is an event driven framework, you my friend, have events... so, I think that this sample code might be the kind of thing that you are looking for.
    #!/usr/bin/perl -w use strict; use POE; use POE::Session; use POE::Wheel::FollowTail; sub usage { "test.pl file_to_watch timeout [debug]\n"; } my $file_name = shift(@ARGV) or die usage(); my $time_out = shift(@ARGV) or die usage(); my $debug = shift(@ARGV) || 0; my $to_match = "I AM LOOKING FOR THIS"; my $session = POE::Session->create( inline_states => { #Called upon POE Kernel initialization, this method #is what we use to set up our environment. _start => sub { my ($heap, $session) = @_[ HEAP, SESSION ]; print "Starting parent session ".$session->ID."\n" if ($debug >= 1); #POE::Wheel::FollowTail is one of those great POE #modules. It basically will sit there and emit an #event every time that a "record" has been added to the #file. Since I did not give it a Filter, it is just #going to read line by line, but you could do some crazy #things with it :) $heap->{tail} = POE::Wheel::FollowTail->new( Filename => $file_name, PollInterval => 1, InputEvent => "input_event", ); }, #This is the event that will be called every time the wheel #emits an InputEvent. It's just a cheesy skeleton, but it #should give you a good idea as to what *you* need to do. input_event => sub { my ($kernel, $heap, $input) = @_[ KERNEL, HEAP, ARG0 ]; print "Input: $input" if ($debug >= 2); if ($input =~ /$to_match/o) { #IMPORTANT: this is where I'm setting the delay event $kernel->delay(do_action => $time_out); print "input matched in P::W::F::InputEvent\n" if $debug; } else { print "input did not match in P::W::F::InputEvent\n" if $debug; } }, #After every line that matches, I will be called after #$time_out time :) do_action => sub { print "I am doing some action!\n" if $debug; }, }, ); $poe_kernel->run();
      Awesome ... i only have one nit-pick. ;)

      Change the input match regex from:

      $input =~ /$to_match/o
      to:
      $input =~ /\Q$to_match\E/
      This will allow you to match metachars as regular characters. I also (rather cargo-cultish-ly) avoid using the o modifier. ask tye :D

      eduardo++

      maybe this should be added to POE Central?

      jeffa

      L-LL-L--L-LL-L--L-LL-L--
      -R--R-RR-R--R-RR-R--R-RR
      B--B--B--B--B--B--B--B--
      H---H---H---H---H---H---
      (the triplet paradiddle with high-hat)
      

        Ah yes... \Q and \E. I'll be honest, I'm not nearly crafty enough with regular expressions to have thought of that. As far as my perling goes, it has been rather surprisingly devoid of regular expressions. I've probably written 20-30k lines of perl code in my life, and alltogether, I've probably written 50 regular expressions in my code. Ah well, that's what we have maverick for :)

        I am however curious about the /o modifier on the re. I mean, if we know the "event" that the user is looking for, what is the reason for *not* using it? After all, as the code has been specified, it's a non-mutable trigger... is it simply an issue of pre-emptive maintenance coding headache prevention? I don't think I know tye nearly well enough to just badger him needlessly when I have you to thwack about :)

      Nice. POE is indeed a very good way to implement user-space threading.

      $h=$ENV{HOME};my@q=split/\n\n/,`cat $h/.quotes`;$s="$h/." ."signature";$t=`cat $s`;print$t,"\n",$q[rand($#q)],"\n";

        but total overkill for this application.

      Well Eduardo, I took your code, tweaked it a bit, implemented it and when I got it to work I giggled like a little girl! I appreciate your help and pseudocode. I normally wouldn't waste thread space with this thank you but I thought it would be beneficial to everyone to know that POE is very nice!

      Thanks again!

      _ _ _ _ _ _ _ _ _ _
      - Jim
      Insert clever comment here...

Re: 'better mousetrap': how to perform timed event
by dpuu (Chaplain) on Apr 23, 2003 at 04:17 UTC
    Have you looked at POE (or more specifically, the stuff about alarms in POE; or the kernel documentation)? --Dave
      This is actually something suggested to that I intend to check out. I've never used POE before and I am bit apprehensive as to my ability to understand what to do since in the past when I have read POE documentation that the concepts were a bit over my head. I didn't need POE back then so I probably didn't put enough effort into understanding the docs.

      Im going to give that a go one more time.

      _ _ _ _ _ _ _ _ _ _
      - Jim
      Insert clever comment here...

Re: 'better mousetrap': how to perform timed event
by BrowserUk (Patriarch) on Apr 23, 2003 at 03:03 UTC

    Given the restriction on using threads which would be my preferred approach, I think that you could acheive a solution by avoiding the blocking read that is responsible for the indeterminate delays that can screw things up. You might do this by monitoring the length of the file in the while loop and only issuing a read when you are sure that there is enough data there to avoid the block. If you know your lines are alway 100 chars, wait till you have more than that before attempting to read a line. Alternatively, use sysread to get whatever is available into a buffer and extract the lines from there yourself.

    If you store your events in a hash of arrays of events, keyed by the time when they are due to be enacted, and used Timer::HiRes or a 4-arg select statement with a delay of half your desired resolution to prevent the loop from running away with the processor, you are guarenteed to wake up at least once in each timer period and can then easily select those events due for enactment in the current time period. This shift of emphasis away from reading the file controlling the loop to using a timer should allow you to enact the events in a timely manner.

    It's the approach I would try anyway, good luck.


    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.
Re: 'better mousetrap': how to perform timed event
by Abigail-II (Bishop) on Apr 23, 2003 at 07:29 UTC
    It seems like you are doing event driven programming. There are various modules on CPAN that deal with event driven programming, each with their advantages and disadvantages. To name a few: Tk, POE, and Event. There's also Uri's pet project, stem, but I don't think that's on CPAN.

    Abigail

Re: 'better mousetrap': how to perform timed event
by zengargoyle (Deacon) on Apr 23, 2003 at 04:07 UTC

    and some really ugly code for BrowserUKs select suggestion...

    # $data is my filehandle to read from my ($rin,$rout,$win,$wout,$ein,$eout); # for select $rin=$rout=$win=$wout=$ein=$eout= ''; vec($rin, fileno($data), 1) = 1; $ein = $rin; my $last = time; my $heartbeat_interval = 60; my $timeout = 1; # for select while (! $done) { $rout=$wout=$eout= ''; my ($nfound, $timeleft) = select($rout=$rin, $wout=$win, $eout +=$ein, $timeout); if ($nfound and $rout & $rin) { # read from <$data> carefully not to block # count statistics } my $now = time; if (($now - $last) >= $heartbeat_interval) { $last = $now; # do something every heartbeat_interval seconds } }
Re: 'better mousetrap': how to perform timed event
by BrowserUk (Patriarch) on Apr 23, 2003 at 11:28 UTC

    Thought I'd put the words into some code as I thought of a use for it:)

    #! perl -slw use strict; use constant LOGFILENAME => 'your logfile name goes here'; use constant CHUNKSIZE => 100; # Vary this to suit your application. open my $log, '<', LOGFILENAME or die $!; # Should use the constants here, but its easier to remember the number +s than # remember the names of the constants and which module to pull to impo +rt them. seek $log, 0, 2; my($lastsize, %events) = (-s LOGFILENAME); while(1) { my $size = -s LOGFILENAME; if ($size > $lastsize + CHUNKSIZE) { # Has the file grown (enough) +? my $line = <$log>; #Read the next available line my $now = time; # Record the time print $now, ' : ', $line; # Just for testing $lastsize += length $line; # remember the new position. if ($line =~ m[ICMP]) { # Check for condition # Schedule an event of met. push @{ $events{$now + 20} }, ['This condition occured 20 seconds ago', $line]; } elsif ($line =~ m[:137]) { push @{ $events{$now + 10} }, ['This condition occured 10 seconds ago', $line]; } } # Should give accuracy to within 1 sec by your systems clock. select undef, undef, undef,0.5; my $now=time; if (exists $events{$now}) { # Check for iminant events for my $event (@{ $events{$now} }) { # Process them all print $event->[0], $/, $event->[1]; } delete $events{$now}; # Remove them when done } }

    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.
Re: 'better mousetrap': how to perform timed event
by Necos (Friar) on Apr 24, 2003 at 17:04 UTC
    Going on tachyon's idea, would it be possible to do something like this?
    while (1) { $new_size = -s $file; if ($new_size > $old_size) { # $old_size is defined before this lo +op if ( event_occured() ) { sleep($map_vTime); do_your_thing(); $old_size = $new_size; } $old_size = $new_size; } sleep($normal_sleep_interval); }
    I would figure, if the size of your log file changes, then you can easily seek to the point in your file that has changed (you do have the old file size), read the data from it (which should be fast if the new data is small), and decide if your event has indeed occured. If it's not your event, just fall out and go back to your polling. If it is, do your magic.

    Hope that helps some.

    Update: Not too long after my initial reply, I noticed that BrowserUK posted a much more elegant solution (maybe just a bit overkill?). The general idea still remains the same: check the filesize of your log. YMMV. Good luck.

    Theodore Charles III
    Network Administrator
    Los Angeles Senior High
    email->secon_kun@hotmail.com
    perl -e "map{print++$_}split//,Mdbnr;"
      While your thoughts are decent a problem still persists. I probably haven't made this too clear, however. I cannot block during the read of the logfile. To do so would delay actions requested outside of the vote session by $map_vTimelimit seconds which would just be poor service to everybody trying to do things on the server during that vote session.

      Im thinking that the POE idea is going to be my best bet. The sample code Eduardo posted looks quite promising. Because what I am looking for is a ''backgrounded timer'' which doesn't block that I can monitor for completion so when it is done counting I can proceed to call a sub-routine.

      _ _ _ _ _ _ _ _ _ _
      - Jim
      Insert clever comment here...

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (2)
As of 2024-04-20 03:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found