File Coverage

blib/lib/Proc/NiceSleep.pm
Criterion Covered Total %
statement 143 217 65.9
branch 61 132 46.2
condition 6 38 15.7
subroutine 22 30 73.3
pod 12 24 50.0
total 244 441 55.3


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