Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can I pause and then resume a call to `sleep`

Calling sleep(10) means to sleep for the specified number of seconds. When I type "sleep 10" I suppose to wait 10 seconds but when I'm using CTRL-Z (or sending SIGTSTP) right after sleep command, it won't stop the "timer" (or the counter) even though that the process has stopped.

I can see via jobs that the status of sleep has changed to STOPPED but if I'll wait 10 seconds and then send this process to the foreground it will finish immediately, even though it run less then 10 seconds.

So, my question is how can I stop sleep timer from running??

Update:: I Understand now that sleep is using wall-clock time, so how can I implement sleep with a user cpu time

like image 345
Matt. Stroh Avatar asked Nov 21 '22 08:11

Matt. Stroh


1 Answers

sleep(3) returns the number of seconds remaining if it was interrupted by a signal, but seconds are poor granularity, so it's better to use clock_nanosleep(2).

clock_nanosleep also has the advantage of allowing you to specify what kind of clock you want to sleep with - there are at least 7 different clocks, each useful in different circumstances. Chances are you want CLOCK_MONOTONIC though. (Note that you use no CPU time while sleeping, so you definitely don't want CLOCK_PROCESS_CPUTIME_ID (it would be meaningful in a multithreaded process))

Quoting the latter man page:

   If the call is interrupted by a signal handler, clock_nanosleep() fails
   with the error EINTR.  In addition, if remain is not  NULL,  and  flags
   was not TIMER_ABSTIME, it returns the remaining unslept time in remain.
   This value can then be used to call clock_nanosleep()  again  and  com‐
   plete a (relative) sleep.

Edit: I ended up writing a program demonstrating what you need to do, as well as the difference between the various clocks:

/*
    Requires adding
        -pthread -lrt
    to your command line.
 */
#define _POSIX_C_SOURCE 200112L

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

struct all_times
{
    struct timespec realtime;

#ifdef CLOCK_REALTIME_COARSE
    struct timespec realtime_coarse;
#endif

    struct timespec monotonic;

#ifdef CLOCK_MONOTONIC_COARSE
    struct timespec monotonic_coarse;
#endif

#ifdef CLOCK_MONOTONIC_RAW
    struct timespec monotonic_raw;
#endif

#ifdef CLOCK_BOOTTIME
    struct timespec boottime;
#endif

#ifdef CLOCK_PROCESS_CPUTIME_ID
    struct timespec process_cputime_id;
#endif

#ifdef CLOCK_THREAD_CPUTIME_ID
    struct timespec thread_cputime_id;
#endif

    struct timespec clock_getcpuclockid;

    struct timespec pthread_getcpuclockid;
};

void get_all_times(struct all_times *times)
{
    clockid_t clock;
    struct timespec *spec;

    memset(times, '\0', sizeof(*times));

    clock = CLOCK_REALTIME;
    spec = &times->realtime;
    clock_gettime(clock, spec);

#ifdef CLOCK_REALTIME_COARSE
    clock = CLOCK_REALTIME_COARSE;
    spec = &times->realtime_coarse;
    clock_gettime(clock, spec);
#endif

    clock = CLOCK_MONOTONIC;
    spec = &times->monotonic;
    clock_gettime(clock, spec);

#ifdef CLOCK_MONOTONIC_COARSE
    clock = CLOCK_MONOTONIC_COARSE;
    spec = &times->monotonic_coarse;
    clock_gettime(clock, spec);
#endif

#ifdef CLOCK_MONOTONIC_RAW
    clock = CLOCK_MONOTONIC_RAW;
    spec = &times->monotonic_raw;
    clock_gettime(clock, spec);
#endif

#ifdef CLOCK_BOOTTIME
    clock = CLOCK_BOOTTIME;
    spec = &times->boottime;
    clock_gettime(clock, spec);
#endif

#ifdef CLOCK_PROCESS_CPUTIME_ID
    clock = CLOCK_PROCESS_CPUTIME_ID;
    spec = &times->process_cputime_id;
    clock_gettime(clock, spec);
#endif

#ifdef CLOCK_THREAD_CPUTIME_ID
    clock = CLOCK_THREAD_CPUTIME_ID;
    spec = &times->thread_cputime_id;
    clock_gettime(clock, spec);
#endif

    clock_getcpuclockid(0, &clock);
    spec = &times->clock_getcpuclockid;
    clock_gettime(clock, spec);

    pthread_getcpuclockid(pthread_self(), &clock);
    spec = &times->pthread_getcpuclockid;
    clock_gettime(clock, spec);
}

void get_all_res(struct all_times *times)
{
    clockid_t clock;
    struct timespec *spec;

    memset(times, '\0', sizeof(*times));

    clock = CLOCK_REALTIME;
    spec = &times->realtime;
    clock_getres(clock, spec);

#ifdef CLOCK_REALTIME_COARSE
    clock = CLOCK_REALTIME_COARSE;
    spec = &times->realtime_coarse;
    clock_getres(clock, spec);
#endif

    clock = CLOCK_MONOTONIC;
    spec = &times->monotonic;
    clock_getres(clock, spec);

#ifdef CLOCK_MONOTONIC_COARSE
    clock = CLOCK_MONOTONIC_COARSE;
    spec = &times->monotonic_coarse;
    clock_getres(clock, spec);
#endif

#ifdef CLOCK_MONOTONIC_RAW
    clock = CLOCK_MONOTONIC_RAW;
    spec = &times->monotonic_raw;
    clock_getres(clock, spec);
#endif

#ifdef CLOCK_BOOTTIME
    clock = CLOCK_BOOTTIME;
    spec = &times->boottime;
    clock_getres(clock, spec);
#endif

#ifdef CLOCK_PROCESS_CPUTIME_ID
    clock = CLOCK_PROCESS_CPUTIME_ID;
    spec = &times->process_cputime_id;
    clock_getres(clock, spec);
#endif

#ifdef CLOCK_THREAD_CPUTIME_ID
    clock = CLOCK_THREAD_CPUTIME_ID;
    spec = &times->thread_cputime_id;
    clock_getres(clock, spec);
#endif

    clock_getcpuclockid(0, &clock);
    spec = &times->clock_getcpuclockid;
    clock_getres(clock, spec);

    pthread_getcpuclockid(pthread_self(), &clock);
    spec = &times->pthread_getcpuclockid;
    clock_getres(clock, spec);
}

void diff_time(const struct timespec *start, const struct timespec *end, struct timespec *diff)
{
    diff->tv_sec = end->tv_sec - start->tv_sec;
    diff->tv_nsec = end->tv_nsec - start->tv_nsec;
    if (diff->tv_nsec < 0)
    {
        diff->tv_nsec += 1000 * 1000 * 1000;
        diff->tv_sec--;
    }
    assert (diff->tv_sec >= 0);
    assert (diff->tv_nsec >= 0);
}

void diff_all_times(const struct all_times *start, const struct all_times *end, struct all_times *diff)
{
    diff_time(&start->realtime, &end->realtime, &diff->realtime);
    diff_time(&start->realtime, &end->realtime, &diff->realtime);

#ifdef CLOCK_REALTIME_COARSE
    diff_time(&start->realtime_coarse, &end->realtime_coarse, &diff->realtime_coarse);
#endif

    diff_time(&start->monotonic, &end->monotonic, &diff->monotonic);

#ifdef CLOCK_MONOTONIC_COARSE
    diff_time(&start->monotonic_coarse, &end->monotonic_coarse, &diff->monotonic_coarse);
#endif

#ifdef CLOCK_MONOTONIC_RAW
    diff_time(&start->monotonic_raw, &end->monotonic_raw, &diff->monotonic_raw);
#endif

#ifdef CLOCK_BOOTTIME
    diff_time(&start->boottime, &end->boottime, &diff->boottime);
#endif

#ifdef CLOCK_PROCESS_CPUTIME_ID
    diff_time(&start->process_cputime_id, &end->process_cputime_id, &diff->process_cputime_id);
#endif

#ifdef CLOCK_THREAD_CPUTIME_ID
    diff_time(&start->thread_cputime_id, &end->thread_cputime_id, &diff->thread_cputime_id);
#endif

    diff_time(&start->clock_getcpuclockid, &end->clock_getcpuclockid, &diff->clock_getcpuclockid);

    diff_time(&start->pthread_getcpuclockid, &end->pthread_getcpuclockid, &diff->pthread_getcpuclockid);
}

void print_all_times(const struct all_times *start, const struct all_times *end, const struct all_times *diff, const struct all_times *res)
{
    printf("%-27s %15s %-9s %15s %-9s %5s %-9s %5s %-9s\n", "clock", "", "start", "", "end", "", "diff", "", "res");

    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_REALTIME", (long long)start->realtime.tv_sec, start->realtime.tv_nsec, (long long)end->realtime.tv_sec, end->realtime.tv_nsec, (long long)diff->realtime.tv_sec, diff->realtime.tv_nsec, (long long)res->realtime.tv_sec, res->realtime.tv_nsec);

    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_REALTIME", (long long)start->realtime.tv_sec, start->realtime.tv_nsec, (long long)end->realtime.tv_sec, end->realtime.tv_nsec, (long long)diff->realtime.tv_sec, diff->realtime.tv_nsec, (long long)res->realtime.tv_sec, res->realtime.tv_nsec);

#ifdef CLOCK_REALTIME_COARSE
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_REALTIME_COARSE", (long long)start->realtime_coarse.tv_sec, start->realtime_coarse.tv_nsec, (long long)end->realtime_coarse.tv_sec, end->realtime_coarse.tv_nsec, (long long)diff->realtime_coarse.tv_sec, diff->realtime_coarse.tv_nsec, (long long)res->realtime_coarse.tv_sec, res->realtime_coarse.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_REALTIME_COARSE");
#endif

    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_MONOTONIC", (long long)start->monotonic.tv_sec, start->monotonic.tv_nsec, (long long)end->monotonic.tv_sec, end->monotonic.tv_nsec, (long long)diff->monotonic.tv_sec, diff->monotonic.tv_nsec, (long long)res->monotonic.tv_sec, res->monotonic.tv_nsec);

#ifdef CLOCK_MONOTONIC_COARSE
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_MONOTONIC_COARSE", (long long)start->monotonic_coarse.tv_sec, start->monotonic_coarse.tv_nsec, (long long)end->monotonic_coarse.tv_sec, end->monotonic_coarse.tv_nsec, (long long)diff->monotonic_coarse.tv_sec, diff->monotonic_coarse.tv_nsec, (long long)res->monotonic_coarse.tv_sec, res->monotonic_coarse.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_MONOTONIC_COARSE");
#endif

#ifdef CLOCK_MONOTONIC_RAW
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_MONOTONIC_RAW", (long long)start->monotonic_raw.tv_sec, start->monotonic_raw.tv_nsec, (long long)end->monotonic_raw.tv_sec, end->monotonic_raw.tv_nsec, (long long)diff->monotonic_raw.tv_sec, diff->monotonic_raw.tv_nsec, (long long)res->monotonic_raw.tv_sec, res->monotonic_raw.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_MONOTONIC_RAW");
#endif

#ifdef CLOCK_BOOTTIME
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_BOOTTIME", (long long)start->boottime.tv_sec, start->boottime.tv_nsec, (long long)end->boottime.tv_sec, end->boottime.tv_nsec, (long long)diff->boottime.tv_sec, diff->boottime.tv_nsec, (long long)res->boottime.tv_sec, res->boottime.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_BOOTTIME");
#endif

#ifdef CLOCK_PROCESS_CPUTIME_ID
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_PROCESS_CPUTIME_ID", (long long)start->process_cputime_id.tv_sec, start->process_cputime_id.tv_nsec, (long long)end->process_cputime_id.tv_sec, end->process_cputime_id.tv_nsec, (long long)diff->process_cputime_id.tv_sec, diff->process_cputime_id.tv_nsec, (long long)res->process_cputime_id.tv_sec, res->process_cputime_id.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_PROCESS_CPUTIME_ID");
#endif

#ifdef CLOCK_THREAD_CPUTIME_ID
    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "CLOCK_THREAD_CPUTIME_ID", (long long)start->thread_cputime_id.tv_sec, start->thread_cputime_id.tv_nsec, (long long)end->thread_cputime_id.tv_sec, end->thread_cputime_id.tv_nsec, (long long)diff->thread_cputime_id.tv_sec, diff->thread_cputime_id.tv_nsec, (long long)res->thread_cputime_id.tv_sec, res->thread_cputime_id.tv_nsec);
#else
    printf("%-27s (not available)\n", "CLOCK_THREAD_CPUTIME_ID");
#endif

    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "clock_getcpuclockid", (long long)start->clock_getcpuclockid.tv_sec, start->clock_getcpuclockid.tv_nsec, (long long)end->clock_getcpuclockid.tv_sec, end->clock_getcpuclockid.tv_nsec, (long long)diff->clock_getcpuclockid.tv_sec, diff->clock_getcpuclockid.tv_nsec, (long long)res->clock_getcpuclockid.tv_sec, res->clock_getcpuclockid.tv_nsec);

    printf("%-27s %15lld.%09ld %15lld.%09ld %5lld.%09ld %5lld.%09ld\n", "pthread_getcpuclockid", (long long)start->pthread_getcpuclockid.tv_sec, start->pthread_getcpuclockid.tv_nsec, (long long)end->pthread_getcpuclockid.tv_sec, end->pthread_getcpuclockid.tv_nsec, (long long)diff->pthread_getcpuclockid.tv_sec, diff->pthread_getcpuclockid.tv_nsec, (long long)res->pthread_getcpuclockid.tv_sec, res->pthread_getcpuclockid.tv_nsec);
}

void signal_handler(int sig)
{
    (void)sig;
    /*
        We don't actually need to do anything, just the presence of the
        signal handler is enough to make `clock_nanosleep` return.

        However, because somebody requested that we stop, we *should*
        listen to them and actually stop.
    */
    raise(SIGSTOP);
}

void do_sleep(struct timespec *total)
{
    int not_errno;
    struct sigaction act;
    memset(&act, '\0', sizeof(act));
    act.sa_handler = signal_handler;
    /* TODO - it is impossible to catch SIGSTOP, is there another way? */
    sigaction(SIGTSTP, &act, NULL);
    sigaction(SIGTTIN, &act, NULL);
    sigaction(SIGTTOU, &act, NULL);
    /*
        Note: synchronous methods of signal handling do *not* work here.
        The `clock_nanosleep` will just resume silently in that case.
        Using `sigtimedwait` does not directly give is a `remain` value.
    */
    do
    {
        /* Important note: clock_nanosleep does *not* use `errno`. */
        not_errno = clock_nanosleep(CLOCK_MONOTONIC, 0, total, total);
    }
    while (not_errno == EINTR);
}



static void die(const char *msg)
{
    printf("%s\n", msg);
    exit(1);
}

static void parse_time(char *str, struct timespec *spec)
{
    unsigned long long sec, nsec, multiplier;
    char *end;
    if (!isdigit(str[0]))
    {
        die("Non-numeric character at start of duration.");
    }
    errno = 0;
    sec = strtoull(str, &end, 10);
    spec->tv_sec = sec;
    if (errno || (unsigned long long)spec->tv_sec != sec)
    {
        die("Out-of-range duration.");
    }
    if (*end == '\0')
    {
        spec->tv_nsec = 0;
        return;
    }
    if (*end != '.')
    {
        die("Non-numeric character in duration.");
    }
    ++end;
    multiplier = 100 * 1000 * 1000;
    nsec = 0;
    while (*end)
    {
        unsigned long long digit = *end - '0';
        if (digit >= 10)
        {
            die("Non-numeric character in fractional duration.");
        }
        nsec += multiplier * digit;
        multiplier /= 10;
        ++end;
        /* TODO instead of truncating extra precision, round up? */
    }
    spec->tv_nsec = nsec;
}

int main(int argc, char **argv)
{
    struct timespec total;
    struct all_times start, end, diff, res;

    if (argc != 2 || argv[1][0] == '-')
    {
        die("Usage: ./nanosleep sss[.mmmuuunnn]");
    }
    parse_time(argv[1], &total);

    get_all_res(&res);
    get_all_times(&start);
    do_sleep(&total);
    get_all_times(&end);

    diff_all_times(&start, &end, &diff);
    print_all_times(&start, &end, &diff, &res);
    return 0;
}

Output:

$ ./nanosleep 1.2
clock                                       start                     end             diff            res      
CLOCK_REALTIME                   1461281943.302055558      1461281944.502279160     1.200223602     0.000000001
CLOCK_REALTIME                   1461281943.302055558      1461281944.502279160     1.200223602     0.000000001
CLOCK_REALTIME_COARSE            1461281943.299600851      1461281944.499668121     1.200067270     0.004000000
CLOCK_MONOTONIC                      130817.112863848          130818.313087604     1.200223756     0.000000001
CLOCK_MONOTONIC_COARSE               130817.110408795          130818.310476065     1.200067270     0.004000000
CLOCK_MONOTONIC_RAW                  130809.723951252          130810.924108013     1.200156761     0.000000001
CLOCK_BOOTTIME                       198571.683842245          198572.884067547     1.200225302     0.000000001
CLOCK_PROCESS_CPUTIME_ID                  0.002856240               0.002900410     0.000044170     0.000000001
CLOCK_THREAD_CPUTIME_ID                   0.002857132               0.002903159     0.000046027     0.000000001
clock_getcpuclockid                       0.002857981               0.002905128     0.000047147     0.000000001
pthread_getcpuclockid                     0.002858526               0.002908051     0.000049525     0.000000001
like image 144
o11c Avatar answered Nov 24 '22 00:11

o11c