Firstly, I want to thank you. It's rare case to get interesting long reply.
I 100% agree with importance of all these guidelines. In my work I use all these guidelines except portable limits (because I've no needs and no ability to test my code on non-linux systems and also in many cases make code portable mean make it slow).
I also agree about my code quality in resource control: CPU - this code was just an example and not really useful because it's too slow (for my tasks at least). But at start of this thread I say "improved, simplifyed and optimized". :-)
About protable code... I don't see any other realization for limiting CPU load issue. My solution based on using things like fork() and signals. I suppose this solution are not really portable, but should work on all/most *NIX systems. And I've seen other people asking about this feature. Really, any who execute long-running process which is working instead of waiting for some event need this feature. And using plain sleep() to free some CPU is ugly solution because it's very hard to know "how much to sleep" to free some CPU and at some time do it work as quickly as possible. So, I think it better to have *NIX-only solution than have no solution.
Here is my current code. I don't see any more possible improvements in main logic, but interface will change because I will package it as module or pragma. Can you please point me to anything not adequate to guidelines except porting one?
# SYNOPSYS
#
# # start new alarm (fork new process)
# $alarm_id = alarm_start(0.01, \$counter);
#
# # stop alarm (kill forked process)
# alarm_stop($alarm_id);
#
# # sleep without been interrupted by signals
# sleepy(0.5);
#
# # limit CPU usage to 10%
# Limit_CPU();
#
use Time::HiRes qw(time alarm setitimer ITIMER_PROF);
{ my %ALARM;
sub alarm_start {
my ($PARENT, $time, $counter) = ($$, @_);
return unless $time > 0;
return unless my ($n) =
grep { not exists $ALARM{$_} } "NUM50" .. "NUM59";
$SIG{$n} = ref($counter) eq "SCALAR" ? sub { $$counter++ } :
ref($counter) eq "CODE" ?
sub { local $SIG{$n}="IGNORE"; &$counter } :
return;
return $n if $ALARM{$n} = fork();
$0 .= " ALARM $n";
$SIG{ALRM} = sub { kill $n, $PARENT or exit };
alarm($time, $time);
CORE::sleep 1 while 1;
}
sub alarm_stop {
return unless exists $ALARM{$_[0]};
local $SIG{CHLD} = $SIG{$_[0]} = "IGNORE";
kill 9, $ALARM{$_[0]} and waitpid delete $ALARM{$_[0]}, 0;
}}
sub sleepy {
my $alarm = alarm_start($_[0], \(my $wakeup)) or
die "sleepy: alarm failed"; # die or return?
CORE::sleep 1 until $wakeup;
alarm_stop($alarm);
}
use constant CPUMAX => 10; # percent, max CPU load by this process
use constant INT => 0.1; # sec, how often check for CPU overload
{ my ($real, $prof, $wait);
$SIG{PROF} = sub { $prof++ };
sub Limit_CPU {
setitimer(ITIMER_PROF, INT, INT);
alarm_start(INT, sub {
$wait = ($prof*(100/CPUMAX) - ++$real) / (1/INT);
return if INT > $wait;
sleepy($wait);
$real += int( $wait * (1/INT) );
});
}}
|