๐Ÿ˜ด Suspending Execution โ€” Sleeping

๐Ÿ˜ด Suspending Execution โ€” Sleeping
Chapter 23 | Part 4 of 9 โ€” sleep() vs nanosleep() โ€” When & How to Sleep
๐Ÿ• sleep()
Second precision
๐Ÿ”ฌ nanosleep()
Nanosecond precision
๐Ÿ“œ Standard
POSIX.1b

What Is Process Sleeping?

Sleeping suspends a process (or thread) for a fixed amount of time. The kernel puts the process in an unrunnable state and wakes it when the interval elapses โ€” or earlier if a signal arrives.

Linux provides two main sleep functions: the simple sleep() and the higher-precision nanosleep().

๐Ÿ“Š sleep() vs nanosleep() โ€” Side-by-Side
Feature sleep() nanosleep()
Header <unistd.h> <time.h>
Precision Whole seconds only Up to 1 nanosecond
Return on interrupt Remaining seconds (unsigned int) -1 + errno=EINTR + remaining in remain
Signal interactions May interact with alarm() on some systems Safe to mix with alarm() / setitimer()
Linux implementation Calls nanosleep() internally Direct syscall
Portability concern Some systems use alarm() โ€” avoid mixing POSIX.1b โ€” widely supported

๐Ÿ“‹ Function Signatures
#include <unistd.h>

/* Sleep for 'seconds' seconds. Returns remaining seconds if
   interrupted by a signal, or 0 on completion. */
unsigned int sleep(unsigned int seconds);

/* ----------------------------------------- */
#define _POSIX_C_SOURCE 199309
#include <time.h>

struct timespec {
    time_t tv_sec;   /* Seconds          */
    long   tv_nsec;  /* Nanoseconds 0..999999999 */
};

/* Sleep for interval specified in 'request'.
   If interrupted, returns -1 with errno=EINTR,
   and 'remain' (if non-NULL) holds the unslept time. */
int nanosleep(const struct timespec *request,
              struct timespec *remain);
/* Returns 0 on completion, -1 on error/interrupt */

๐Ÿ”„ nanosleep() โ€” Signal-Restart Loop

When a signal interrupts nanosleep(), restart it with the remaining time to complete the full sleep interval.

nanosleep(request, remain)
โ†“ completes normally
Returns 0 โ†’ done โœ“
โ†“ interrupted by signal (returns -1, errno=EINTR)
remain has unslept time
โ†“
Copy remain โ†’ request
โ†“
nanosleep(request, remain) โ† loop back

๐Ÿ’ป Example 1: Basic sleep() Usage
#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main(void) {
    time_t start, end;

    printf("Sleeping for 3 seconds...\n");
    start = time(NULL);

    unsigned int remaining = sleep(3);
    end = time(NULL);

    if (remaining == 0) {
        printf("Woke up normally. Elapsed: %ld seconds\n",
               (long)(end - start));
    } else {
        printf("Sleep interrupted! %u seconds remaining.\n",
               remaining);
    }

    /* sleep() on Linux does NOT interact with alarm() since
       it calls nanosleep() internally โ€” safe to mix */
    return 0;
}

/* Output: Sleeping for 3 seconds...
           Woke up normally. Elapsed: 3 seconds */

๐Ÿ’ป Example 2: nanosleep() โ€” Sub-Second Sleeping
#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <time.h>
#include <errno.h>

int main(void) {
    struct timespec req, rem;

    /* Sleep for 250 milliseconds = 0 seconds + 250,000,000 ns */
    req.tv_sec  = 0;
    req.tv_nsec = 250000000L;  /* 250 ms */

    printf("Sleeping for 250ms...\n");

    if (nanosleep(&req, &rem) == 0) {
        printf("Slept successfully.\n");
    } else {
        if (errno == EINTR) {
            printf("Interrupted! Remaining: %ld.%09ld s\n",
                   rem.tv_sec, rem.tv_nsec);
        }
    }

    /* Sleep for 1.5 seconds */
    req.tv_sec  = 1;
    req.tv_nsec = 500000000L;  /* 0.5 s */
    printf("Sleeping for 1.5 seconds...\n");
    nanosleep(&req, NULL);
    printf("Done.\n");

    return 0;
}

/* Output:
   Sleeping for 250ms...
   Slept successfully.
   Sleeping for 1.5 seconds...
   Done.
*/

๐Ÿ’ป Example 3: Restartable nanosleep() โ€” Full Sleep Despite Signals

Loop to ensure the full sleep interval completes even when interrupted by signals.

#define _POSIX_C_SOURCE 199309
#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

/* A signal handler that does nothing โ€” just interrupts nanosleep */
static void sigint_handler(int sig) {
    printf("\n[Signal %d caught โ€” will restart sleep]\n", sig);
}

/**
 * Sleep for 'secs' seconds + 'nsecs' nanoseconds.
 * Automatically restarts if interrupted by signals.
 * Returns: 0 on success, -1 on real error.
 */
int reliable_sleep(long secs, long nsecs) {
    struct timespec req, rem;
    req.tv_sec  = secs;
    req.tv_nsec = nsecs;

    while (nanosleep(&req, &rem) == -1) {
        if (errno == EINTR) {
            /* Signal interrupted us โ€” resume with remaining time */
            req = rem;
        } else {
            return -1;   /* Real error */
        }
    }
    return 0;
}

int main(void) {
    struct sigaction sa;

    /* Allow Ctrl-C (SIGINT) to interrupt but not kill */
    sa.sa_handler = sigint_handler;
    sa.sa_flags   = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);

    printf("Sleeping for 5 seconds. Press Ctrl-C to test interruption...\n");

    if (reliable_sleep(5, 0) == 0) {
        printf("Full 5-second sleep completed.\n");
    }

    return 0;
}

/* Even if you press Ctrl-C multiple times, the program
   eventually completes the full 5-second sleep.

   Sample run:
   Sleeping for 5 seconds. Press Ctrl-C to test interruption...
   [Signal 2 caught โ€” will restart sleep]
   [Signal 2 caught โ€” will restart sleep]
   Full 5-second sleep completed.
*/

โš ๏ธ Nanosecond Precision vs Real Accuracy
The precision/accuracy gap

nanosleep() accepts nanosecond values, but actual delivery is rounded up to the jiffy boundary (same as setitimer). High-res timers close this gap.

Signal-restart rounding accumulation

Each restart of nanosleep rounds up again. With very high signal rates, total sleep can drift. Use clock_nanosleep(TIMER_ABSTIME) to avoid this (covered in Part 6).

๐ŸŽ“ Interview Questions
Q1. What does sleep() return if interrupted by a signal?

The number of remaining (unslept) seconds. If it completed normally, it returns 0.

Q2. What is the advantage of nanosleep() over sleep()?

Nanosecond precision, explicit remaining-time via the remain argument, and guaranteed independence from alarm()/setitimer() signals (POSIX says it must not use signals internally).

Q3. Why should you NOT mix sleep() with alarm() on portable code?

On some implementations, sleep() is implemented using alarm() and SIGALRM. Calling both can corrupt timer state. On Linux specifically it is safe (sleep calls nanosleep), but for portability avoid mixing.

Q4. Write a reliable_sleep() function that handles signal interruptions.

Loop calling nanosleep() in a while loop. When it returns -1 with errno==EINTR, copy remain back to request and call again. Exit the loop when nanosleep() returns 0.

Q5. What is tv_nsec’s valid range in struct timespec?

0 to 999,999,999 (inclusive). It represents nanoseconds within the current second. Values outside this range cause nanosleep() to fail with EINVAL.

Next: POSIX Clocks โ€” clock_gettime() & clock_settime() โ†’

Part 5: POSIX Clocks โ† Part 3: Blocking Timeouts

Leave a Reply

Your email address will not be published. Required fields are marked *