File Coverage

blib/lib/Proc/NiceSleep.pm
Criterion Covered Total %
statement 144 218 66.0
branch 61 132 46.2
condition 6 38 15.7
subroutine 22 30 73.3
pod 12 24 50.0
total 245 442 55.4


line stmt bran cond sub pod time code
1             package Proc::NiceSleep;
2              
3             #############################################################################
4             # Proc::NiceSleep - intelligent sleeping library
5             #
6             # Copyright (c) 2002-2012 Josh Rabinowitz, licensed the same as
7             # perl itself, see COPYRIGHT below
8             #
9             # originally generated by joshr 20020216
10             #
11             # see full pod perldocs below after __END__ or via perldoc NiceSleep.pm
12             #
13             # $Id: NiceSleep.pm,v 1.36 2012/12/23 23:28:58 joshr Exp $
14             #############################################################################
15              
16 7     7   103516 use 5.004; # tested this far back and up to 5.10.0
  7         25  
  7         268  
17 7     7   43 use strict; # please
  7         13  
  7         306  
18             #use warnings; # doesn't exist in 5.004
19              
20             require Exporter;
21             #use AutoLoader qw(AUTOLOAD); # we don't use this yet
22              
23             # We do 'use vars' like this so we can work nicely in old versions of perl
24 7     7   37 use vars qw($VERSION);
  7         18  
  7         391  
25              
26             $VERSION = '0.90';
27              
28             # these are 'public'
29 7     7   34 use vars qw ( %EXPORT_TAGS @EXPORT_OK @ISA );
  7         12  
  7         957  
30              
31             @ISA = qw(Exporter);
32              
33             # This allows declaration use Proc::NiceSleep ':all';
34             %EXPORT_TAGS = ( 'all' => [ qw(
35             nice maybe_sleep max_load sleep_factor min_run_time min_sleep_time
36             over_load_min_sleep_time over_load_sleep_drift
37             load_function
38             maybesleep maxload sleepfactor minruntime minsleeptime
39             yield
40             ) ] );
41              
42             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
43              
44             # these are private.
45 7         19585 use vars qw ( $_sleepfactor $_minruntime $_minsleeptime $_maxload
46             $_over_load_minsleeptime $_over_load_sleep_drift
47             $_totalsleeptime $_lastsleeptime $_lastmaybesleeptime $_lastloadchecktime
48             $_numtimesslept $_numtimesmaybesleepcalled $_numtimesloadchecked
49             @_lastloadavgs $_starttime
50             $_havetimehires $_havetimehires $_havesetpriority
51             $_haveprocprocesstable $_havesyscpuload $_havebsdresource
52 7     7   37 $_loadfunction);
  7         13  
53             # variables prefixed by _ are intended to be private, here we explain them
54             #$_lastsleeptime; # the last time we slept, from time()
55             #$_lastloadchecktime; # the last time we checked load()
56             #@_lastloadavgs; # the values from _GetCpuLoad last time we checked
57             #$_starttime; # the time we finished init()
58             #$_sleepfactor; # 1.0 means to sleep 1.0 times as long as we 'run'
59             # 0.0 means don't sleep based on fraction of tmie
60             #$_minruntime; # how long we run before considering yielding
61             #$_minsleeptime; # minimum time to sleep, if we do
62             #$_maxload; # the maximum 1-minute avg system load we yield at,
63             # if supported and Sys::CpuLoad works
64             # # does not include time sleeping in maybe_sleep()
65             #$_totalsleeptime; # how long we slept in maybe_sleep(),
66             # # in apparent wallclock seconds
67             #$_havetimehires; # do we have Time::HiRes ?
68             #$_haveprocprocesstable; # do we have Proc::ProcessTable?
69             #$_havesyscpuload; # do we have Sys::CpuLoad?
70             #$_havesetpriority; # do we have a setpriority() call?
71              
72             # all through we use Time::HiRes or built-in versions of
73             # time() and sleep(), and get microsecond res ... or not.
74              
75             #############################################################################
76             # Preloaded methods go here.
77             #############################################################################
78              
79             # nice() this renices the process, like /bin/nice, if it can.
80             # if passed an integer parameter (between -20 to 20 inclusive)
81             # it attempts to set the priority and returns the priority
82             # it tried to set the process to.
83             # if called without a parameter, returns what it thinks its priority is
84             # does not work on win32 (always should return 0); use maybe_sleep() ! :)
85             sub nice {
86 2 100   2 1 16 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  1         5  
87 2         18 my $param = shift;
88             # 'man setpriority' on rh7.2: The setpriority call returns 0 if there
89             # is no error, or -1 if there is.
90             # even though man page says the above, setpriority(0,0,5) returns 1
91             # on RH7.2
92 2 100       6 if (defined($param)) {
93 1         2 $param = int($param); # pass me an int, holmes
94 1 50 33     20 if ($_havesetpriority && setpriority(0,0,$param) != -1) {
95 1         3 return $param;
96             } else {
97 0         0 return 0;
98             }
99             }
100 1 50       7 return ($_havesetpriority ? getpriority(0,0) : 0);
101             # no param, return what we think the nice value is.
102             }
103              
104             # keep calling maybe_sleep() until maxsleep seconds have passed
105             # or it doesn't need to sleep anymore.
106             # pass '0' for infinite yield
107             sub yield {
108 0   0 0 0 0 my $maxsleep = shift || 0;
109 0         0 my $t1 = time();
110 0         0 my $sleeptime = 0;
111 0         0 my $slept = 0;
112 0   0     0 do {
      0        
113 0         0 $slept = maybe_sleep();
114 0         0 $sleeptime += $slept;
115             } while ($slept && (!$maxsleep || $sleeptime <= $maxsleep));
116 0         0 return $sleeptime;
117             }
118              
119             # checks to see if we should sleep.
120             # returns how long we think we slept if we did, 0 otherwise
121             sub maybe_sleep {
122 6 50   6 1 1017 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
123 6         8 $_numtimesmaybesleepcalled++;
124 6 50       26 my $t1 = ($_havetimehires ? Time::HiRes::time() : CORE::time());
125 6         12 $_lastmaybesleeptime = $t1;
126 6         12 my $timepassed = $t1 - $_lastsleeptime;
127 6         14 my ($timetosleep, $timeslept) = (0, 0);
128 6 50 33     33 if ($_minruntime && $timepassed < $_minruntime) { return 0; }
  0         0  
129 6 50       22 if ($_sleepfactor) {
130 6 100       18 if ($_totalsleeptime == 0) {
131 2         7 $timetosleep = $_sleepfactor * $timepassed;
132             #print "Debug1: timetosleep = $timetosleep\n";
133             } else {
134 4         8 my $totalruntime = $t1 - $_starttime - $_totalsleeptime;
135             # we've actually seen $totalruntime be zero when there's no
136             # Time::HiRes
137 4 50       12 if ($totalruntime > 0) { # avoid divide by zero below
138 4         7 my $actualratio = $_totalsleeptime / $totalruntime;
139 4 50       14 if ($actualratio < $_sleepfactor) {
140 0         0 $timetosleep =
141             $_sleepfactor * $totalruntime - $_totalsleeptime;
142             }
143             }
144 4 50       12 $timetosleep = 0 if ($timetosleep < 0);
145             #print "Debug2: timetosleep = $timetosleep\n";
146             }
147             }
148 6 100 66     38 if ($_maxload && ($t1 - $_lastloadchecktime >= 0.5)) {
149             # we only check the load a max of about once per half second
150 1         4 my (@loads) = _GetCpuLoad(); # (1minavg, 5minavg, 15minavg)
151 1         3 @_lastloadavgs = @loads;
152 1 50 33     7 if ($loads[0] && $loads[0] > $_maxload) {
153             # sleep if load is too high
154 1         40 my $drift = rand($_over_load_sleep_drift);
155 1         6 $timetosleep = MAX(
156             $_over_load_minsleeptime + $drift,
157             4 * ($loads[0] - $_maxload) + $drift, # diff between current load and max load
158             $timetosleep,
159             $_minsleeptime,
160             );
161             }
162             }
163 6 100       16 if ($timetosleep) { # we should sleep... snore....
164 2 50 33     12 if ($_minsleeptime && $timetosleep < $_minsleeptime) {
165 0 0       0 if ($timetosleep <= 0) { $timetosleep = 0; } # can't be neg
  0         0  
166 0         0 $timetosleep = $_minsleeptime;
167             }
168 2 50       7 if($_havetimehires) {
169 2         2287370 Time::HiRes::sleep($timetosleep); # yield the system via sleep
170             } else {
171 0         0 $timetosleep = int($timetosleep + .5); # round off.
172 0 0       0 if ($timetosleep <= 0) { $timetosleep = 1; } # can't be neg or 0
  0         0  
173 0         0 CORE::sleep($timetosleep); # actually yield the system via sleep
174             }
175 2 50       57 my $t2 = ($_havetimehires ? Time::HiRes::time() : CORE::time());
176 2         10 my $actualsleeptime = $t2 - $t1;
177 2         10 $_totalsleeptime += $actualsleeptime; # how long we slept
178 2         13 $_lastsleeptime = $t2; # record this
179 2         8 $timeslept = $actualsleeptime; # for return
180 2         6 $_numtimesslept++;
181             }
182 6         22 return $timeslept; # in case they wonder. this is how long we slept
183             }
184             # sets or gets, depending on whether it gets param or not
185             sub sleep_factor {
186 4 100   4 1 33 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  1         5  
187 4         22 my $param = shift;
188 4 50       13 if (defined($param)) {
189 4 50       15 $param = 0 if ($param < 0); # don't allow negative sleep_factor
190 4         10 $_sleepfactor = $param;
191             }
192 0         0 else { return $_sleepfactor; }
193             }
194             # sets or gets, depending on whether it gets param or not
195             sub min_sleep_time {
196 0 0   0 1 0 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
197 0         0 my $param = shift;
198 0 0       0 if (defined($param)) {
199 0 0       0 $param = 0 if ($param < 0); # don't allow negative value
200 0         0 $_minsleeptime = $param;
201             }
202 0         0 else { return $_minsleeptime; }
203             }
204             # sets or gets, depending on whether it gets param or not
205             sub min_run_time {
206 4 100   4 1 810 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  2         9  
207 4         44 my $param = shift;
208 4 50       16 if (defined($param)) {
209 4 50       18 $param = 0 if ($param < 0); # don't allow negative value
210 4         13 $_minruntime = $param;
211             }
212 0         0 else { return $_minruntime; }
213             }
214             # sets or gets, depending on whether it gets param or not
215             sub max_load {
216 1 50   1 1 10 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  1         4  
217 1         23 my $param = shift;
218 1 50       3 if (defined($param)) {
219 1 50       5 $param = 0 if ($param < 0); # don't allow negative value
220 1         9 $_maxload = $param;
221             }
222 0         0 else { return $_maxload; }
223             }
224             # sets or gets, depending on whether it gets param or not
225             sub over_load_min_sleep_time {
226 1 50   1 1 5 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
227 1         2 my $param = shift;
228 1 50       3 if (defined($param)) {
229 1 50       2 $param = 0 if ($param < 0); # don't allow negative value
230 1         2 $_over_load_minsleeptime = $param;
231             }
232 0         0 else { return $_over_load_minsleeptime; }
233             }
234             # sets or gets, depending on whether it gets param or not
235             sub over_load_sleep_drift {
236 2 50   2 1 9 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
237 2         4 my $param = shift;
238 2 50       6 if (defined($param)) {
239 2 50       6 $param = 0 if ($param < 0); # don't allow negative value
240 2         6 $_over_load_sleep_drift = $param;
241             }
242 0         0 else { return $_over_load_sleep_drift; }
243             }
244             # returns a ref to a hash with data about the progress...
245             # for informational purposes only. return values subject to change.
246             sub Dump {
247 1 50   1 1 15 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
248 1         4 my %hash = (
249             #HAVE_TIME_HIRES => $_havetimehires,
250             #HAVE_PROC_PROCESSTABLE => $_haveprocprocesstable,
251             #HAVE_SYS_CPULOAD => $_havesyscpuload,
252             #HAVE_SETPRIORITY => $_havesetpriority,
253             LAST_LOAD_CHECK_TIME => dump_clock($_lastloadchecktime),
254             LAST_LOAD_AVERAGES => join(" ", @_lastloadavgs),
255             LAST_SLEEP_TIME => dump_clock($_lastsleeptime),
256             LAST_MAYBE_SLEEP_TIME => dump_clock($_lastmaybesleeptime),
257             MAX_LOAD => $_maxload,
258             #LOAD_FUNCTION => $_loadfunction,
259             MIN_RUN_TIME => $_minruntime,
260             MIN_SLEEP_TIME => $_minsleeptime,
261             SLEEP_FACTOR => $_sleepfactor,
262             TOTAL_RUN_TIME =>
263             (Proc::NiceSleep::time() - $_starttime - $_totalsleeptime),
264             TOTAL_SLEEP_TIME => $_totalsleeptime,
265             NUM_TIMES_SLEPT => $_numtimesslept,
266             NUM_TIMES_LOAD_CHECKED => $_numtimesloadchecked,
267             NUM_TIMES_MAYBE_SLEEP_CALLED => $_numtimesmaybesleepcalled,
268             # extra comma here is ok, cool!
269             );
270 1         6 return \%hash;
271             }
272             # this is for informational purposes only. Data and its output subject to change
273             # written to remove dependence on Data::Dumper in our examples
274             sub DumpText {
275             # a convenient method to ascii-ify return of Dump() nicely for reporting.
276 0 0   0 1 0 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
277 0         0 my $hashref = Dump();
278 0         0 my $str = "";
279 0         0 for my $e (sort keys(%$hashref)) { # the entry name
280 0         0 my $v = $$hashref{$e}; # the value
281 0 0 0     0 if (!defined($v)) {
    0          
282 0         0 $str .= sprintf(" %-28s: (undef)\n", $e);
283             } elsif ($v =~ /^([0-9.]+)$/ && (int($v) != $v) ) {
284 0         0 $str .= sprintf(" %-28s: %1.3f\n", $e, $v);
285             } else {
286 0         0 $str .= sprintf(" %-28s: %s\n", $e, $v);
287             }
288             }
289 0         0 return $str; # returns a nice, ascii text page of the name/vals :)
290             }
291              
292             # if called with param, sets the load
293             sub load_function {
294 2 50   2 1 12 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
295 2 100       5 if (@_) {
296 1         2 $_loadfunction = shift;
297 1         3 } else { return $_loadfunction; }
298             }
299              
300             # time() and sleep() are so test programs don't have to test for Time::HiRes
301             # they do hi-res if possible. They are also shown used in example.pl,
302             # but are not documented as public... should they be, kind reader?
303 18 50   18 0 246 sub time { ($_havetimehires ? Time::HiRes::time() : CORE::time()); }
304 4 50   4 0 2000569 sub sleep { ($_havetimehires ? Time::HiRes::sleep(@_) : CORE::sleep(@_)); }
305              
306             #############################################################################
307             # THESE ARE FOR TEMPORARY REVERSE SUPPORT. Soon we'll give warnings,
308             # eventually we'll remove them
309             #############################################################################
310 0     0 0 0 sub maybesleep { return maybe_sleep(@_); }
311 0     0 0 0 sub maxload { return max_load(@_); }
312 0     0 0 0 sub sleepfactor { return sleep_factor(@_); }
313 0     0 0 0 sub minruntime { return min_run_time(@_); }
314 0     0 0 0 sub minsleeptime { return min_sleep_time(@_); }
315              
316             #############################################################################
317             # THINGS AFTER HERE (until perldocs) ARE PRIVATE METHODS !!!
318             #############################################################################
319 3 100   3 0 225 sub dump_clock { return (($_[0]) ? scalar(localtime($_[0])) : 0); }
320 1 100   1 0 3 sub MAX { my $max = shift; for(@_) { $max = $_ if $_ > $max; } return $max; }
  1         3  
  3         9  
  1         4  
321              
322             sub init { # intended to be private
323             # try to load Time::HiRes and ProcessTable
324              
325 6     6 0 10 eval{ require Time::HiRes };
  6         6908  
326 6 50       12706 if ($@) { $_havetimehires = 0; } else { $_havetimehires = 1; }
  0         0  
  6         16  
327             # eval alone can't seem to import sleep() and time() from Time::HiRes.
328             # 'use Time::HiRes qw(sleep time);' from here doesn't seem to get
329             # sleep() and time() imported outside this function, either.
330              
331             #eval{require Proc::ProcessTable; }; # we don't use this.... yet.
332             #if ($@) { $_haveprocprocesstable = 0 } else { $_haveprocprocesstable = 1 }
333              
334 6         16 eval{
335 6         2381 require Sys::CpuLoad;
336 0         0 my @l=Sys::CpuLoad::load();
337 0 0 0     0 die unless (@l > 2 && defined($l[0]) && $l[0] =~ /^\s*[0-9]*\.?[0-9]+$/);
      0        
338             };
339 6 50       38 if ($@) { $_havesyscpuload = 0 } else { $_havesyscpuload = 1 }
  6         17  
  0         0  
340              
341 6         10 eval{ my $pri=getpriority(0,0); setpriority(0,0,$pri); };
  6         73  
  6         46  
342             # check for setpriority() and setpriority() with a (hopefully) no-op
343 6 50       20 if ($@) { $_havesetpriority = 0 } else { $_havesetpriority = 1 }
  0         0  
  6         12  
344              
345 6         10 eval{ require BSD::Resource; };
  6         2041  
346 6 50       29 if ($@) { $_havebsdresource = 0; } else { $_havebsdresource = 1; }
  6         13  
  0         0  
347              
348 6         13 $_sleepfactor = 0.1; # the default
349 6         12 $_minruntime = 0.0;
350             # can be meaningfully this short if we have Time::HiRes
351 6         11 $_minsleeptime = 0; # no 'minimum' time to sleep by default
352 6         11 $_over_load_minsleeptime = 3.5; # 4 was default from v0.77
353 6         11 $_over_load_sleep_drift = 1;
354 6         11 $_maxload = 0; # 0 means don't watch loads
355 6         11 $_loadfunction = undef; # reset this too
356 6         18 @_lastloadavgs = (0,0,0);
357 6         21 Proc::NiceSleep::reset_all();
358             }
359              
360             sub reset_all {
361 7     7 1 23 $_numtimesslept = 0;
362 7         16 $_numtimesmaybesleepcalled = 0;
363 7         12 $_numtimesloadchecked = 0;
364 7         13 $_totalsleeptime = 0;
365 7         11 $_lastloadchecktime = 0;
366 7         21 $_lastmaybesleeptime = 0;
367 7         41 $_lastsleeptime =
368             $_starttime = Proc::NiceSleep::time();
369             }
370              
371             # Invariant(): attempt to check that the vars are self-consistent.
372             # returns 1 if OK, 0 if object 'bad'. Not intended to be called often
373             sub Invariant { # intended to be private. Used in tests
374 1 50   1 0 8 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  1         4  
375             # check obvious things:
376             # can we load a method/func from each mod we loaded?
377 1 50       21 if ($_havetimehires) { # this will die if we can't load func
378 1         4 my $t = Time::HiRes::time();
379 1         217 Time::HiRes::sleep(0.0001);
380             }
381 1 50       7 if ($_havesyscpuload) { # this will die if we can't load func
382 0         0 my @l = Sys::CpuLoad::load();
383             }
384             # if we think we have Time::HiRes, is time() fractional? Inverse?
385             # we used to test that we did or didn't get fractional times, but
386             # it turns out that just cause you have Time::HiRes doesn't mean you
387             # get fractional times and sleeps.
388 1 50       4 if ($_havetimehires) { # could still be integer-based
389             #my ($t1, $t2) = (time(), time()); # at least ONE shouldn't be int
390             #return 0 if ($t1 == int($t1) && $t2 == int($t2));
391             } else {
392             # we assume no version of perl has a sub-second time() in CORE (!)
393 0         0 my ($t1, $t2) = (CORE::time(), CORE::time()); # both should be ints
394 0 0 0     0 return 1 if ($t1 != int($t1) || $t2 != int($t2));
395             # but really, even if times are floating point, everything is probably ok
396             }
397 1         7 return 1; # that's all we test... seems ok!
398             }
399              
400             # on some machines, Sys::CpuLoad won't get the load, but it's still
401             # possible to fetch. So we try harder.
402             sub _GetCpuLoad {
403 1 50   1   3 unless (defined($_lastsleeptime)) { init(); } # autoinit on first use
  0         0  
404 1         2 $_numtimesloadchecked++;
405 1         9 $_lastloadchecktime = Proc::NiceSleep::time();
406 1 50       6 if ($_loadfunction) { # use the load function if we can
407 1         3 my (@L) = &${_loadfunction}();
408 1 50       7 return @L if @L;
409             }
410 0           my @loads = (0,0,0);
411 0 0         if ($_havesyscpuload) {
412 0           @loads = Sys::CpuLoad::load();
413             }
414 0 0         if ($loads[0] == 0) { # either the load is that low, or Sys::CpuLoad::load() is
415             # just returning (0,0,0) as it does on OSX 10.3 as of 11/2004
416              
417 0           local( %ENV ); # for taint safety
418 0           @ENV{qw(PATH BASH_ENV)} = ( "/usr/bin:/bin", "");
419              
420 0 0         open(UPTIME, "/usr/bin/uptime |") || return @loads;
421 0           my $out = ; # read one line.
422 0           chomp($out);
423 0 0         close(UPTIME) || return @loads; # this could fail if pipe didn't work
424 0 0 0       if ($out && $out =~ /([0-9.]+)\s+([0-9.]+)\s+([0-9.]+)\s*$/) {
425 0           @loads = ($1, $2, $3);
426             }
427             }
428 0           return @loads;
429             }
430              
431             # Autoload methods go after =cut, and are processed by the autosplit program.
432             # we have none ... yet.
433             #############################################################################
434             1;
435              
436             __END__