Search code examples
cposix

How to make "long tv_nsec" and "time_t tv_sec" compatible?


I am writting a wrapper function sleep_new() for clock_nanosleep() which would make thread suspension easier for me.

// POSIX.1-2017 is what compiler is confined to.
#define _XOPEN_SOURCE 700

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

// POSIX headers.

// Other headers
#include "sleep_new.h"

void sleep_new(long value, const char unit[3]){
    
    // Create a timespec structure and set it's members.
    // Members are added together!!! So to set time "1.5 s" we set "t.tv_sec = 1" and "t.tv_sec = 500000000".
    // Members ".tv_sec" and ".tv_nsec" represent unit and not value!
    struct timespec sleep_time;

    // Set flags i.e. TIMER_ABSTIME to 0 to use relative instead of absolute time.
    int flags = 0;

    // Choose the clock i.e. CLOCK_MONOTONIC is a "clock_id" for the clock started at system start.
    int clock_id = CLOCK_MONOTONIC;

    // Set timespec structure's members according to the chosen unit.
    if (!strcmp(unit, "s")) {
        sleep_time.tv_sec = value;
        sleep_time.tv_nsec = 0;
    }
    else if (!strcmp(unit, "ns")){
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = value;
    }
    else if (!strcmp(unit, "us")){
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = value * 1000;
    }
    else if (!strcmp(unit, "ms")){
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = value * 1000000;
    }
    else{
        puts("Unit not supported - choose between: s, ms, us, ns\n");
    }

    // Because last argument is NULL in case of error, remaining time is not stored in "t".
    clock_nanosleep(clock_id, flags, &sleep_time, NULL);

}

int main(int argc, char *argv[])
{
    // Counter.
    uint8_t i;

    for(i = 0; i < 256; i++){
        // Stdout is newline  buffered. This is why we either have to include `\n` at the end or flush() it manually.
        // So uncomment one example A or B.

        // A
        //printf("%d\n", i);

        // B
        printf("%d, ", i);
        fflush(stdout);

        // Because last argument is NULL in case of error, remaining time is not stored in "t".
        sleep_new(1000, "ms");
    }

    return 0;

}

If I call this function with sleep_new(1, "s") or sleep_new(2, "s") it works fine, because it sets the sleep_time.tv_nsec = 0; and sleep_time.tv_sec = value;.

In any other scenarios i.e. sleep_new(1000, "ms") something is wrong and sleep is not applied. I debugged application and values are applied to the timespec members just fine but clock_nanosec() just ignores them.

enter image description here

I am using type long for the value because I read in the POSIX here where header time.h defines timespec structure's members tv_nsec who needs long and member tv_sec who uses time_t which is in turn defined in header sys/types.h like this:

time_t shall be an integer type.

So because long can also hold int values I expected this to work, but it doesn't. Does anyone have any suggestion?


Solution

  • The tv_nsec is the number of nanoseconds in a second - 1000 * 1000000 nanoseconds is too much. That's 1 second! tv_nsec should range from 0 to 999999999. The proper calculation could look like:

        sleep_time.tv_sec = value / 1000;
        sleep_time.tv_nsec = (value % 1000) * 1000000;