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...
Re: 'better mousetrap': how to perform timed event
by tachyon (Chancellor) on Apr 23, 2003 at 02:09 UTC
|
# 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
| [reply] [d/l] |
|
| [reply] |
|
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
| [reply] |
|
|
(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();
| [reply] [d/l] |
|
$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)
| [reply] [d/l] [select] |
|
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 :)
| [reply] |
|
|
|
|
$h=$ENV{HOME};my@q=split/\n\n/,`cat $h/.quotes`;$s="$h/."
."signature";$t=`cat $s`;print$t,"\n",$q[rand($#q)],"\n";
| [reply] [d/l] |
|
| [reply] |
|
|
| [reply] |
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 | [reply] |
|
| [reply] |
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.
| [reply] |
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
| [reply] |
Re: 'better mousetrap': how to perform timed event
by zengargoyle (Deacon) on Apr 23, 2003 at 04:07 UTC
|
# $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
}
}
| [reply] [d/l] |
Re: 'better mousetrap': how to perform timed event
by BrowserUk (Patriarch) on Apr 23, 2003 at 11:28 UTC
|
#! 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.
| [reply] [d/l] |
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;" | [reply] [d/l] [select] |
|
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...
| [reply] |
|
|