Adam has asked for the wisdom of the Perl Monks concerning the following question:

I wanted to write a simple one-liner to send keep-alive packets over an ssh connection that would otherwise time out and disconnect me. I figured the simple solution is that when I wasn't actively using the terminal, I could run my script to make it "do something"*.
So I did:
bash-2.02$ perl -e '{warn scalar localtime,$/;sleep 60;redo}' Wed Mar 21 10:27:12 2001 Wed Mar 21 10:28:12 2001 Wed Mar 21 10:29:12 2001 ^C
Well, I looked at that and said to my self, "That would look nicer if the seconds field was just 00." So I tried:
bash-2.02$ perl -e 'while(time%60){}{warn scalar localtime,$/;sleep 60 +;redo}' Wed Mar 21 10:39:00 2001 Wed Mar 21 10:39:59 2001 Wed Mar 21 10:40:59 2001 Wed Mar 21 10:41:59 2001 ^C
That through me for a bit of a loop, and I thought about it and remembered that sleep is imprecise.
From Camel2: On some systems, the function sleeps till the "top of the second"
Ok, I can handle that, but then the "top of the second" ought to be 60-61 seconds, right? Nope.
From sleep: On some older systems, it may sleep up to a full second less than what you requested
Well, I can work with that, so after playing with select, I landed on:
bash-2.02$ perl -e '{while(time%60){}warn scalar localtime,$/;sleep 59 +;redo}' Wed Mar 21 10:58:00 2001 Wed Mar 21 10:59:00 2001 Wed Mar 21 11:00:00 2001 Wed Mar 21 11:01:00 2001 ^C
But what I don't understand, and perhaps someone can enlighten me, is why "top of the second" would round down like that, instead of up. Either I don't understand the phrase, or on some older systems it is just a different behaviour. The system, by the way, is running SunOS 5.7. Also, why didn't it drift before I added the initialization? I thought that maybe the initialization was putting it on the edge, so I added a select statement to pause it for an additional tenth of a second, and ten minutes later it had drifted off again. This leads me to think that the system clock it-self might be at fault. Ideas? Insights?
*Why would you want keep alive packets? Why would a system time you out? Well actually, I'm behind a firewall that kills any tcp session where no data is passed for a couple of minutes (ie, the sequence numbers don't change). Not nice when you go to ask someone a question and come back to find that you have been disconnect. Technically, any amount of data would prevent the disconnect, but the time was just more esthetically pleasing.

Replies are listed 'Best First'.
(tye)Re: Top of the second?
by tye (Sage) on Mar 22, 2001 at 01:28 UTC

    Old implementations of sleep just woke up once every second (inside the operating system) so saying sleep(1) means "wake me up the next time you wake up", which will be in something less than one second (almost one second if it had just recently woken up and almost immediately if it has been almost a second since it woke up).

    Update: After chatting with Adam, he had one case that can't be explained by this. That was when his display ended in ":00" for quite a while and then shifted to ":59". The only explanation I can come up with for that is that the system clock isn't consistantly running at the same speed as the "sleep metronome". This is often true on systems where the "sleep metronome" is based on the "uptime clock" (which monotonously marches forward at something close to but not quite exactly real time) while the "time of day clock" has various types of adjustments applied to it in an attempt to keep it in sync with the major television networks.

            - tye (but my friends call me "Tye")
Re: Top of the second?
by AgentM (Curate) on Mar 22, 2001 at 02:28 UTC
    The resolution for sleep is not guaranteed in any manner by POSIX. In fact, on certain (crappy) systems, it may even change during the running of a program due to process load (by yet again undefinable amounts). sleep is most always (if NOT always) implemented using alarm(1), so your problem stems back to that function. sleep should be used for coarse wait control- hence, it is only useful for "user-level" time manipulation. A user can't determine the difference between a second and a second +1/6 second.

    For lingering on a socket, you can use the SO_LINGER socket option if that's available to you, or you can continue to use your method with a bogus select where POSIX guarantees a certain time resolution due to constraints on the timeval structure. Sun seems to follow POSIX (to a certain extent), so that would work better for you in this case. Good luck!

    AgentM Systems nor Nasca Enterprises nor Bone::Easy nor Macperl is responsible for the comments made by AgentM. Remember, you can build any logical system with NOR.
Re: Top of the second?
by merlyn (Sage) on Mar 22, 2001 at 01:45 UTC
    On linux, this always shows a time ending in 0:
    my $PERIOD = 10; while (1) { my $mod = time % $PERIOD; sleep $PERIOD - $mod; print scalar localtime, "\n"; }
    Try something like that.

    -- Randal L. Schwartz, Perl hacker

      This (mostly) works as long as your sleep metronome and time of day clock are in sync and your system isn't busy enough to cause too much delay between execution of the different lines of code. Not always true:

      Sleep metronome: | | | | | | Time of day: | 07 | 08 | 09 | 10 | 11 | 12 | A B C A: time=...09 $mod=9 B: sleep 10-9 C: localtime=...:09

      You can see lots of ways to move points around on that drawing to get different results as well.

              - tye (but my friends call me "Tye")
        Yeah, so at worst, it sleeps to 01, which is not a problem because you might not have been swapped in until 01 anyway. {grin}

        -- Randal L. Schwartz, Perl hacker

Re: Top of the second?
by Anonymous Monk on Mar 22, 2001 at 01:44 UTC
    If you want to send keep alive messages across an ssh connection, check the documentation for your ssh implementation, man ssh2_config for me reveals a KeepAlive directive which will have ssh send keep alive messages over the connection so you don't have to worry about it timing out. If you have the TMOUT variable set, it could be bash which is disconnecting you and not your ssh connection.
      I know the source of the time out: I spoke with the administrator for our firewall. I only have the problem at work, so that isolates it as well. As for having ssh handle the keep alives, I didn't think to check that, but after reading your post I went exploring. Sigh. My ssh terminal doesn't have that option. It is a Windows implementation, not a *nix. Good idea though.