Previous Page
Next Page

8.12. Sleeping

Avoid a raw select for non-integer sleeps.

Perl's built-in sleep function will only pause your program for an integer number of seconds, even if you give it a floating-point duration:

    sleep 1.5;          # same as sleep(int(1.5)), so sleeps 1 second

Worse still, if you ask it to sleep only a fraction of a second, it's effectively a no-op:

    sleep 0.5;          # same as sleep(int(0.5)), so sleeps 0 seconds

Some systems are not capable of sleeping for fractions of a second, but if yours is, the easiest way to achieve that is to use the Time::HiRes module (which comes standard in Perl 5.8 and later):

    use Time::HiRes qw( sleep );
    sleep 0.5;          
# now sleeps half a second

For even more accuracy (within the limitations of your underlying platform), you can import the Time::HiRes::usleep( ) function instead and specify the length of your nap as an integral number of microseconds:

    use Time::HiRes qw( usleep );
    usleep 500_001;     
# now sleeps just over half a second

Prior to the availability of the Time::HiRes module, the usual way to sleep for fractions of seconds was to use a side effect of Perl's built-in select function. The select function is supposed to poll sets of I/O streams to determine which of them are ready for reading or writing, and which have exceptions pending[*].

[*] If that brief explanation didn't help, don't worry about it. What select is supposed to do isn't important here; what's important is how hackers have perverted it to further their own evil designs.

But the most useful part of this builtin turned out to be its fourth argument, which is supposed to tell select how long to conduct its poll before timing out. It was quickly realized that because this timeout value could be specified in fractions of a second, if select was called with a timeout value but without any streams to poll, like so:

    select undef, undef, undef, $duration;

then it would just sit there until the timeout expired, thereby sleeping for that fractional duration.

So if Time::HiRes isn't available, you can always sleep half a second by calling:

    select undef, undef, undef, 0.5;

But finding something like that crawling around loose in your code is just plain nasty. It uses a mysterious and poorly understood builtin in a sneaky and improbable way. If you need to fall back on select-based sleeping, at least encapsulate the unpleasantness cleanly:

    sub sleep_for {
        my ($duration) = @_;
        select undef, undef, undef, $duration;

# and then
... sleep_for(0.5);

Wrapping the select call in a subroutine will have negligible impact on the accuracy of the sleep interval. On most systems, the subroutine call overhead will be much smaller than the smallest interval for which select can reliably pause.

    Previous Page
    Next Page