Evolution of sleep() C Function

Discussion in 'C' started by poornaMoksha, Oct 9, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    In this article, we will discuss different implementations of sleep functions that had flaws to understand how sleep function evolved. Before discussing the sleep function implementations, lets first understand briefly the following two functions :

    alarm() function

    From Linux Man page :

    SYNOPSIS
    #include <unistd.h>

    unsigned int alarm(unsigned int seconds);

    DESCRIPTION
    alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.

    If seconds is zero, no new alarm() is scheduled.

    In any event any previously set alarm() is canceled.

    RETURN VALUE
    alarm() returns the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously
    scheduled alarm.​
    pause() function

    From Linux man page :

    SYNOPSIS
    #include <unistd.h>

    int pause(void);

    DESCRIPTION
    pause() causes the calling process (or thread) to sleep until a signal is delivered that either terminates the process or causes the invocation of
    a signal-catching function.

    RETURN VALUE
    pause() only returns when a signal was caught and the signal-catching function returned. In this case pause() returns -1, and errno is set to
    EINTR.​

    Now, lets come to the main topic 'sleep()'.

    From Linux Man page :

    SYNOPSIS
    #include <unistd.h>
    unsigned int sleep(unsigned int seconds);

    DESCRIPTION
    sleep() makes the current process sleep until seconds seconds have elapsed or a signal arrives which is not ignored.

    RETURN VALUE
    Zero if the requested time has elapsed, or the number of seconds left to sleep.​

    Can anyone think of simulating a 'sleep()' function with the help of alarm() and pause() functions?

    Different earlier Implementations



    Ok guyz, I have an Idea.
    What if we do something like :

    Code:
    /*  pseudo code for my implementation of sleep() */
    
    
    //Signal handler for SIGALRM
    
    void sig_alrm(int sig)
    {
        /* do some stuff */
    }
    
    // My_sleep()
    unsigned int my_sleep(unsigned int seconds)
    {
        // Set signal handler for SIGALRM
        ...
        ...
        alarm(seconds);
        ...
        pause();
        ...
        ...
    }
    Well, we all think that this will work, so lets convert it into code. Here is the actual piece(the first implementation):

    Code:
    #include      <signal.h>
    #include      <unistd.h>
    static void
    sig_alarm_handler(int sig)
    {
       /* dummy definition, nothing much to do */
    }
    unsigned int
    Mysleep1(unsigned int seconds)
    {
       if (signal(SIGALRM, sig_alarm_handler) == SIG_ERR)
           return(seconds);
       alarm(seconds);        /* start the timer */
       pause();          /* Suspend the process until a signal occurs */
       return(alarm(0)); /* turn off timer, return un-slept time */
    }
    Huff, was not that difficult or was it? :)

    Now, the intelligent viewers would have already started searching for the loopholes as I have already told that this is flawed.

    So, whats the flaw ?

    Well, besides others, the major loop hole lies in the time window between the call to alarm() and the call to pause(). What if this time gap is more than the 'seconds' after which the timer for alarm expires?? The pause() function will be called after the signal handler has been executed and the process will be in suspended state forever(assuming no signal occurs there after). Holy cow!!! That was a good catch by those of you who got it bang on.

    Well, these geeks who actually write these logics do make mistakes and if you believe in yourself, you can also be like them one day. But the truth prevails, you'll still make mistakes as these geeks do. :)

    OK, enough of motivation. When the above limitation was reported, another implementation came along. Here is the piece of code:

    Code:
    #include <setjmp.h>
    #include <signal.h>
    #include <unistd.h>
    
    static jmp_buf env;
    
    static void sig_alarm_handler(int sig)
    {
       longjmp(env, 1);
    }
    
    unsigned int Mysleep2(unsigned int seconds)
    {
       if (signal(SIGALRM, sig_alarm) == SIG_ERR)
           return(seconds);
       if (setjmp(env) == 0) {
           alarm(seconds);     /* start the timer */
           pause();       /* suspend the process until a signal is caught */
       }
       return(alarm(0));     /* turn off timer, return un-slept time */
    }
    Well, this seemed to be a promising one as it cleared off the earlier problem where a process may hang. Here in this implementation, even if the alarm goes off before the function pause() is called then longjmp ensured that process did not hang forever. Well, that smart coding. But unfortunately, a bug exposed in this implementation too as suppose we call Mysleep2() for 3 seconds and in between these 3 seconds, another signal (say generated through CTRL+C by user) is occurs and is being handled by its handler. Then as soon as the 3 second alarm expires, the call to longjmp() from handler of alarm signal will cause the handler of SIGINT(generated by user) to abort abruptly.

    The code foe above explanation is something like this :

    Code:
    static jmp_buf env;
    
    static void sig_alarm_handler(int sig)
    {
       longjmp(env, 1);
    }
    
    unsigned int Mysleep2(unsigned int seconds)
    {
       if (signal(SIGALRM, sig_alarm) == SIG_ERR)
           return(seconds);
       if (setjmp(env) == 0) {
           alarm(seconds);     /* start the timer */
           pause();       /* suspend the process until a signal is caught */
       }
       return(alarm(0));     /* turn off timer, return unslept time */
    }
    
    
    int
    main(void)
    {
        unsigned int          unslept;
        if (signal(SIGINT, sig_int) == SIG_ERR)
            err_sys("signal(SIGINT) error");
        unslept = Mysleep2(5);
        printf("sleep2 returned: %u\n", unslept);
        exit(0);
    }
    static void
    sig_int(int signo)
    {
        /* do some stuff for more than 5 seconds */
    
    }
    Well, nothing is perfect. An assignment for you guys is to find out the latest implementation of sleep() and find any loophole in it. :)

    Conclusion



    In this article we discussed the evolution of sleep() function and how it is being constantly updated with every new loophole being surfaced out. This also shows that programming with signals is not a cake walk :) .
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice