Friday, January 3, 2014

Sleeping for a negative interval in perl

Stumbled on this behavior accidentally when walking through some buggy Perl code. The gist is:
> perl -e 'sleep(1);print "done\n"'
done
> perl -e 'sleep(-1);print "done\n"'
... never completes!
I tried to trove through the perldoc and the underlying linux man page for sleep but couldn't find an explicit explanation. I guess because the underlying sleep method in unistd.h accepts unsigned int, perl's negative 1 is interpreted as 32-bit unsigned integer which is 2^31 - waiting for 2^31 seconds - which is a loong time making us think that it is never completing. To put this theory to test, we tried out something like:
> perl -e 'sleep(-0.0000000000000000000000000000001);print "done\n"'
done
and it completed! Hope this helps someone else as well!
EDIT:
Looks like the fractional example is not a good one - looking through Perl's source code, it plainly seems that sleep is modeled as a macro:
#define PerlProc_sleep(t)    sleep((t))
So the floating point expression was being plainly truncated. A better test case would be 2^32 - which can't be fit in a 32 bit signed integer (the default chosen) and when overflowing would be interpreted as 1 and so on.
> date ; perl -e 'sleep(4294967296);print "done\n"' ; date
Mon Jan  6 02:12:27 EST 2014
done
Mon Jan  6 02:12:27 EST 2014
> date ; perl -e 'sleep(4294967297);print "done\n"' ; date
Mon Jan  6 02:12:34 EST 2014
done
Mon Jan  6 02:12:35 EST 2014
> date ; perl -e 'sleep(4294967298);print "done\n"' ; date
Mon Jan  6 02:12:42 EST 2014
done
Mon Jan  6 02:12:44 EST 2014
EDIT 2:

A colleague of mine pointed out a couple of things. A more straight-forward way of confirming the hypothesis is via strace. Firstly, we need to know the underlying Linux system call that is being invoked by the Perl runtime. strace -v on the perl one-liner of sleep(1) will help you identify that it is nanosleep. strace -v -e nanosleep on the perl one-line of sleep(-1) will show that it will indeed try to sleep for 4294967295 seconds. Secondly, Time::HiRes module of Perl, is a better alternative for such sleeps, because it catches these negative usages of sleep interval with a cocky "negative time not invented yet" croak.

References: