Hang On a Sec, Part I

I learned to program on a microcomputer, a gizmo radically different in software and hardware design from today’s systems. An example of this difference is that if you wanted to write code that paused for a second, you wrote a for loop delay. Such a thing is obnoxiously impractical today — which sounds like a dare!

TRS-80 Model III
On my trusty TRS-80 Model III — yes the one with the TV set for a monitor that frazzled my eyeballs — I knew that writing a FOR loop (in BASIC) counting from one to 1,000 would pause a program for one second. Silly as it sounds, this technique worked. And it was consistent for all TRS-80 Model III computers. Lots of programmers used this trick.

The same trick was employed in the early days of the PC, which was also technically a microcomputer. The PC’s processor was faster, so the delay loops needed to be longer. It wasn’t until multitasking operating systems took over in the late 1990s that writing a delay loop proved to be embarrassingly wrong.

Different processors have different speeds. Not only that, but some programs can be interrupted by other processes. These advances in computing technology are marvelous, but they render the delay loop useless. Further, modern computer hardware features a system clock, which is far more reliable for programming a pause.

Being curious and feeling nostalgic, I wondered how large a loop needed to be on my current computer to pause program execution for one second?

The first step is to see how fast the computer counts between seconds. To do so, I wrote a program that monitors when seconds turn over. The following code fetches the current epoch time, or clock ticks in seconds, then waits for the value to change.

2023_03_18-Lesson-a.c

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now,then;

    /* obtain clock tick Epoch value */
    time(&now);
    printf("%ld\n",now);

    /* pause one second */
    then = now;
    while(then==now)
        time(&now);
    printf("%ld\n",now);

    return(0);
}

Variable now stores the current epoch time, which is also saved in variable then. A while loop spins as long as both now and then are equal. Within the loop, variable now is updated with the current clock tick value. When it changes, the loop ends and the new time is output:

1677951318
1677951319

While this code indicates when the system clock’s seconds turn over, it doesn’t really time an exact second. That’s because the program may start between seconds. No, to time a full second, a second while loop is necessary. Here is the updated code:

2023_03_18-Lesson-b.c

#include <stdio.h>
#include <time.h>

int main()
{
    time_t now,then;

    /* obtain clock tick Epoch value */
    time(&now);

    /* pause one second */
    then = now;
    while(then==now)
        time(&now);
    /* now a new second has begun
       start the next "full second" loop */
    printf("%ld\n",now);
    then = now;
    while(then==now)
        time(&now);
    printf("%ld\n",now);

    return(0);
}

The value of now is output after the firstwhile loop ends at the start of a new second. Then, after the second while loop stops, the next clock tick value is output:

1677951742
1677951743

You may encounter a slight pause before the first line is output. Still, the time between the first line output and the next is one full second (or as close as it can be given other things going on in the system).

The next step is to count the number of iterations for the second while loop. I cover this code update in next week’s Lesson, along with the final for loop code that delays the computer for one second — just like the old days.

2 thoughts on “Hang On a Sec, Part I

  1. This weeks challenge intrigued me, so I gave it a try… thinking to myself, that relying on POSIX.1b interval timers should simplify things, I decided to use clock_gettime() instead of time() – while the latter function only returns the number of seconds since the Epoch, clock_gettime() fills in the fields of a slightly more complicated “timespec” structure:

    struct timespec {
    time_t tv_sec; /* Seconds — ≥ 0 */
    long tv_nsec; /* Nanoseconds — [0, 999999999] */
    };

    When going this route, the only other thing one has to decide is which one of the various clocks to use:

    CLOCK_REALTIME measures real (i.e., wall-clock) time
    CLOCK_MONOTONIC nonsettable clock that represents monotonic time

    As the man pages note »The CLOCK_MONOTONIC clock is not affected by discontinuous jumps in the system time« … for the purpose of this exercise CLOCK_MONOTONIC thus seems the better fit.

    Putting all of the above together, the following should do the trick:

    #define NANOSECONDS_PER_SECOND 1000000000ull

    #define timespec_difference(t1, t2) \
    ((t2).tv_sec*NANOSECONDS_PER_SECOND + (t2).tv_nsec \
    – (t1).tv_sec*NANOSECONDS_PER_SECOND – (t1).tv_nsec)

    struct timespec now, then;
    unsigned long long time_diff;

    (void)clock_gettime (CLOCK_MONOTONIC, &then);
    do {
    (void)clock_gettime (CLOCK_MONOTONIC, &now);
    time_diff = timespec_difference (then, now);
    } while (time_diff < NANOSECONDS_PER_SECOND);

    I uploaded project files for Codelite (Linux) as well as Visual Studio (Windows) on GitHub (should anyone be interested): https://tinyurl.com/2a6w59hx

  2. That’s really cool. I must do a lesson on the clock_* functions in a future post. Thanks!

Leave a Reply