Your Own Time Delay Function

I’ve written about my own delay() function, which uses processor clock ticks to calculate a time delay in milliseconds. Thanks to the clock_gettime() function, I can write a new time-delay function that operates from nanoseconds — or close to it. I hope.

Last week’s Lesson demonstrated how the clock_gettime() function returns a nanosecond value. The call is precise, but measuring the interval between calls is subject to a delay. Therefore, a nanosecond delay function won’t be accurate to a specific nanosecond value. This limitation doesn’t mean I won’t write the thing anyway.

Off the top of my head, I can’t think why someone would want to delay a program for a single nanosecond. And I’m not going to dive into the quagmire of how many clock cycles each CPU instruction takes per nanosecond. Regardless, this Lesson is just for fun.

2023_05_20-Lesson.c

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

#define MAX_N 999999999L

void nano_delay(long ns)
{
    struct timespec res;
    long end;

    /* avoid out of range values */
    if( ns<1 || ns>MAX_N )
        return;

    /* obtain the current value */
    clock_gettime(CLOCK_REALTIME,&res);
    /* calculate end time */
    end = res.tv_nsec + ns;
    /* wait for the end time */
    while(1)
    {
        clock_gettime(CLOCK_REALTIME,&res);
        if( res.tv_nsec > end )
            break;
    }
}

int main()
{
    long delay;

    for( delay=10; delay<MAX_N; delay*=10 )
    {
        printf("Delay = %ld\n",delay);
        nano_delay(delay);
    }

    return(0);
}

The nano_delay() function accepts a long int as its argument, a delay in nanoseconds. An if test immediately weeds out values less than one and greater than the value of MAX_N or 999,999,999.

After the clock_gettime() function fetches the current time, variable end is assigned the current nanosecond value plus the delay value (ns). An eternal while loop spins until the current nanosecond value fetched from clock_gettime() is greater than the end value.

In the main() function, the nano_delay() function is called using powers of ten values: 10, 100, 1000, and so on. Here’s output from a sample run:

Delay = 10
Delay = 100
Delay = 1000
Delay = 10000
Delay = 100000
Delay = 1000000
Delay = 10000000
Delay = 100000000

I doubt anyone can notice any pauses inthe output, given that it takes the computer longer to write to the terminal than the delay. The final value may show a barely noticeable pause.

This code contains a flaw that could disrupt the timing. For example, if the current nanosecond value stored in res.tv_nsec is 750,000,000 (commas added for readability). And the delay is 500,000,000, the value of variable end is 1,250,000,000. This value is out of range for res.tv_nsec. The if test in the endless while loop will never be true.

I’ve run the code a few times and not had any issues, though the math puzzles me. I’ll try to address this issue in next week’s post.

3 thoughts on “Your Own Time Delay Function

  1. No biggie + I’m not in principle against using break, but why not use do-while in this case: do { clock_gettime(CLOCK_REALTIME, &r); } while (res.tv_nsec<end);
    (A test for ‘.tv_sec’ could also added quite easily.)

    Also, ‘long ns’ would—in principle—allow one to wait for really long periods of time: (2⁶³ – 1) ÷ (86,400 • 365.24219) ≈ 292 years. In case of huge ‘ns’ values one could try to efficiently wait for sleep(uint32_t seconds); first and only then turn to busy waiting. Alternatively, one could probably type ‘ns’ as an ‘int’ variable… giving a hint to potential callers, that waiting for more than 2 seconds is out of the question.

    (Not too serious with these suggestions, just speculating around.)

  2. For some reason, call it habit, I don’t like junking up a while condition. I know how it works, and it’s a better way to code, but I’m just not in the habit.

    For the data type, I chose long because it matches the tv_nsec member of the timespec structure. I do agree with your thoughts regarding setting it as an int, but I chose to match the existing data structure, again out of habit.

  3. »For some reason, call it habit«
    Touché. I guess every programmer has a few of those ☺

    »because it matches the tv_nsec member of the timespec structure«
    True, with ‘int’ a type conversion would be necessary—didnʼt think about that.

    Probably not really a problem anyway, as it should be quite obvious that nano_delay() is only intended to wait for short periods of time.

Leave a Reply